Commit 7bc42c384518d25ba6d752eef928381dcc0bf650

Authored by jiangwei
2 parents 468d6b81 602a136a

Merge remote-tracking branch 'origin/master' into pms_partplane

Showing 43 changed files with 1315 additions and 1620 deletions
package.json
... ... @@ -76,6 +76,7 @@
76 76 "react-beautiful-dnd": "^13.1.1",
77 77 "react-bmap": "^1.0.131",
78 78 "react-copy-to-clipboard": "^5.1.0",
  79 + "react-d3-tree": "3.6.2",
79 80 "react-document-title": "^2.0.3",
80 81 "react-dom": "^16.14.0",
81 82 "react-image-crop": "^8.6.12",
... ...
pnpm-lock.yaml
... ... @@ -128,6 +128,9 @@ dependencies:
128 128 react-copy-to-clipboard:
129 129 specifier: ^5.1.0
130 130 version: 5.1.0(react@16.14.0)
  131 + react-d3-tree:
  132 + specifier: 3.6.2
  133 + version: 3.6.2(react-dom@16.14.0)(react@16.14.0)
131 134 react-document-title:
132 135 specifier: ^2.0.3
133 136 version: 2.0.3(react@16.14.0)
... ... @@ -2386,6 +2389,22 @@ packages:
2386 2389 resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==}
2387 2390 dev: true
2388 2391  
  2392 + /@bkrem/react-transition-group@1.3.3(react-dom@16.14.0)(react@16.14.0):
  2393 + resolution: {integrity: sha512-nUZaumHu/MMolELv+MhEEQzQtKsnfpbKBHtam/NK53tGICwU19tuffEXW8BLhm9HhQfN1H3+C0bsJv8Z7vzwEA==}
  2394 + peerDependencies:
  2395 + react: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0
  2396 + react-dom: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0
  2397 + dependencies:
  2398 + chain-function: 1.0.1
  2399 + dom-helpers: 3.4.0
  2400 + loose-envify: 1.4.0
  2401 + prop-types: 15.8.1
  2402 + react: 16.14.0
  2403 + react-dom: 16.14.0(react@16.14.0)
  2404 + react-lifecycles-compat: 3.0.4
  2405 + warning: 3.0.0
  2406 + dev: false
  2407 +
2389 2408 /@bloomberg/record-tuple-polyfill@0.0.3:
2390 2409 resolution: {integrity: sha512-sBnCqW0nqofE47mxFnw+lvx6kzsQstwaQMVkh66qm/A6IlsnH7WsyGuVXTou8RF2wL4W7ybOoHPvP2WgIo6rhQ==}
2391 2410  
... ... @@ -3114,6 +3133,10 @@ packages:
3114 3133 '@types/node': 20.8.7
3115 3134 dev: true
3116 3135  
  3136 + /@types/d3-hierarchy@1.1.11:
  3137 + resolution: {integrity: sha512-lnQiU7jV+Gyk9oQYk0GGYccuexmQPTp08E0+4BidgFdiJivjEvf+esPSdZqCZ2C7UwTWejWpqetVaU8A+eX3FA==}
  3138 + dev: false
  3139 +
3117 3140 /@types/eslint-scope@3.7.7:
3118 3141 resolution: {integrity: sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==}
3119 3142 dependencies:
... ... @@ -5936,6 +5959,10 @@ packages:
5936 5959 lazy-cache: 1.0.4
5937 5960 dev: false
5938 5961  
  5962 + /chain-function@1.0.1:
  5963 + resolution: {integrity: sha512-SxltgMwL9uCko5/ZCLiyG2B7R9fY4pDZUw7hJ4MhirdjBLosoDqkWABi3XMucddHdLiFJMb7PD2MZifZriuMTg==}
  5964 + dev: false
  5965 +
5939 5966 /chalk@1.1.3:
5940 5967 resolution: {integrity: sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==}
5941 5968 engines: {node: '>=0.10.0'}
... ... @@ -6187,7 +6214,6 @@ packages:
6187 6214 /clone@2.1.2:
6188 6215 resolution: {integrity: sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==}
6189 6216 engines: {node: '>=0.8'}
6190   - dev: true
6191 6217  
6192 6218 /cloneable-readable@1.1.3:
6193 6219 resolution: {integrity: sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ==}
... ... @@ -6697,6 +6723,14 @@ packages:
6697 6723 resolution: {integrity: sha512-fVjoElzjhCEy+Hbn8KygnmMS7Or0a9sI2UzGwoB7cCtvI1XpVN9GpoYlnb3xt2YV66oXYb1fLJ8GMvP4hdU1RA==}
6698 6724 dev: false
6699 6725  
  6726 + /d3-drag@3.0.0:
  6727 + resolution: {integrity: sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==}
  6728 + engines: {node: '>=12'}
  6729 + dependencies:
  6730 + d3-dispatch: 1.0.6
  6731 + d3-selection: 3.0.0
  6732 + dev: false
  6733 +
6700 6734 /d3-dsv@1.0.10:
6701 6735 resolution: {integrity: sha512-vqklfpxmtO2ZER3fq/B33R/BIz3A1PV0FaZRuFM8w6jLo7sUX1BZDh73fPlr0s327rzq4H6EN1q9U+eCBCSN8g==}
6702 6736 hasBin: true
... ... @@ -6769,6 +6803,11 @@ packages:
6769 6803 resolution: {integrity: sha512-SJ0BqYihzOjDnnlfyeHT0e30k0K1+5sR3d5fNueCNeuhZTnGw4M4o8mqJchSwgKMXCNFo+e2VTChiSJ0vYtXkg==}
6770 6804 dev: false
6771 6805  
  6806 + /d3-selection@3.0.0:
  6807 + resolution: {integrity: sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==}
  6808 + engines: {node: '>=12'}
  6809 + dev: false
  6810 +
6772 6811 /d3-shape@1.3.7:
6773 6812 resolution: {integrity: sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==}
6774 6813 dependencies:
... ... @@ -6790,10 +6829,35 @@ packages:
6790 6829 d3-timer: 1.0.10
6791 6830 dev: false
6792 6831  
  6832 + /d3-transition@3.0.1(d3-selection@3.0.0):
  6833 + resolution: {integrity: sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==}
  6834 + engines: {node: '>=12'}
  6835 + peerDependencies:
  6836 + d3-selection: 2 - 3
  6837 + dependencies:
  6838 + d3-color: 1.4.1
  6839 + d3-dispatch: 1.0.6
  6840 + d3-ease: 1.0.7
  6841 + d3-interpolate: 1.4.0
  6842 + d3-selection: 3.0.0
  6843 + d3-timer: 1.0.10
  6844 + dev: false
  6845 +
6793 6846 /d3-voronoi@1.1.4:
6794 6847 resolution: {integrity: sha512-dArJ32hchFsrQ8uMiTBLq256MpnZjeuBtdHpaDlYuQyjU0CVzCJl/BVW+SkszaAeH95D/8gxqAhgx0ouAWAfRg==}
6795 6848 dev: false
6796 6849  
  6850 + /d3-zoom@3.0.0:
  6851 + resolution: {integrity: sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==}
  6852 + engines: {node: '>=12'}
  6853 + dependencies:
  6854 + d3-dispatch: 1.0.6
  6855 + d3-drag: 3.0.0
  6856 + d3-interpolate: 1.4.0
  6857 + d3-selection: 3.0.0
  6858 + d3-transition: 3.0.1(d3-selection@3.0.0)
  6859 + dev: false
  6860 +
6797 6861 /dagre@0.8.5:
6798 6862 resolution: {integrity: sha512-/aTqmnRta7x7MCCpExk7HQL2O4owCT2h8NT//9I1OQ9vt29Pa0BzSAkR5lwFUcQ7491yVi/3CXU9jQ5o0Mn2Sw==}
6799 6863 dependencies:
... ... @@ -6821,7 +6885,7 @@ packages:
6821 6885 resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==}
6822 6886 engines: {node: '>=0.11'}
6823 6887 dependencies:
6824   - '@babel/runtime': 7.23.2
  6888 + '@babel/runtime': 7.23.8
6825 6889  
6826 6890 /date-format@0.0.0:
6827 6891 resolution: {integrity: sha512-kAmAdtsjW5nQ02FERwI1bP4xe6HQBPwy5kpAF4CRSLOMUs/vgMIEEwpy6JqUs7NitTyhZiImxwAjgPpnteycHg==}
... ... @@ -6991,6 +7055,11 @@ packages:
6991 7055 engines: {node: '>= 0.8'}
6992 7056 dev: true
6993 7057  
  7058 + /dequal@2.0.3:
  7059 + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==}
  7060 + engines: {node: '>=6'}
  7061 + dev: false
  7062 +
6994 7063 /des.js@1.1.0:
6995 7064 resolution: {integrity: sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==}
6996 7065 dependencies:
... ... @@ -7087,6 +7156,12 @@ packages:
7087 7156 /dom-align@1.12.4:
7088 7157 resolution: {integrity: sha512-R8LUSEay/68zE5c8/3BDxiTEvgb4xZTF0RKmAHfiEVN3klfIpXfi2/QCoiWPccVQ0J/ZGdz9OjzL4uJEP/MRAw==}
7089 7158  
  7159 + /dom-helpers@3.4.0:
  7160 + resolution: {integrity: sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==}
  7161 + dependencies:
  7162 + '@babel/runtime': 7.23.8
  7163 + dev: false
  7164 +
7090 7165 /dom-scroll-into-view@1.2.1:
7091 7166 resolution: {integrity: sha512-LwNVg3GJOprWDO+QhLL1Z9MMgWe/KAFLxVWKzjRTxNSPn8/LLDIfmuG71YHznXCqaqTjvHJDYO1MEAgX6XCNbQ==}
7092 7167 dev: false
... ... @@ -7224,7 +7299,7 @@ packages:
7224 7299 peerDependencies:
7225 7300 redux: 4.x
7226 7301 dependencies:
7227   - '@babel/runtime': 7.23.2
  7302 + '@babel/runtime': 7.23.8
7228 7303 flatten: 1.0.3
7229 7304 global: 4.4.0
7230 7305 invariant: 2.2.4
... ... @@ -9144,7 +9219,7 @@ packages:
9144 9219 /history-with-query@4.10.4:
9145 9220 resolution: {integrity: sha512-JnskQK8X+PbRFHSdDAExhoJyhLnlLZL+UuHQuQhys+Se9/ukRDRBWU4JVTjsiIfbv1fcEmR3oqKW56OYmk5M5w==}
9146 9221 dependencies:
9147   - '@babel/runtime': 7.23.2
  9222 + '@babel/runtime': 7.23.8
9148 9223 loose-envify: 1.4.0
9149 9224 query-string: 6.14.1
9150 9225 resolve-pathname: 3.0.0
... ... @@ -9155,7 +9230,7 @@ packages:
9155 9230 /history@4.10.1:
9156 9231 resolution: {integrity: sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==}
9157 9232 dependencies:
9158   - '@babel/runtime': 7.23.2
  9233 + '@babel/runtime': 7.23.8
9159 9234 loose-envify: 1.4.0
9160 9235 resolve-pathname: 3.0.0
9161 9236 tiny-invariant: 1.3.1
... ... @@ -11403,7 +11478,7 @@ packages:
11403 11478 prop-types: ^15.0.0
11404 11479 react: ^0.14.0 || ^15.0.0 || ^16.0.0 || ^17.0.0
11405 11480 dependencies:
11406   - '@babel/runtime': 7.23.2
  11481 + '@babel/runtime': 7.23.8
11407 11482 prop-types: 15.8.1
11408 11483 react: 16.14.0
11409 11484 tiny-warning: 1.0.3
... ... @@ -13207,7 +13282,7 @@ packages:
13207 13282 react: '>=16.9.0'
13208 13283 react-dom: '>=16.9.0'
13209 13284 dependencies:
13210   - '@babel/runtime': 7.23.2
  13285 + '@babel/runtime': 7.23.8
13211 13286 classnames: 2.3.2
13212 13287 dom-align: 1.12.4
13213 13288 rc-util: 5.38.0(react-dom@16.14.0)(react@16.14.0)
... ... @@ -13515,7 +13590,7 @@ packages:
13515 13590 react: '>=16.9.0'
13516 13591 react-dom: '>=16.9.0'
13517 13592 dependencies:
13518   - '@babel/runtime': 7.23.2
  13593 + '@babel/runtime': 7.23.8
13519 13594 classnames: 2.3.2
13520 13595 rc-resize-observer: 1.4.0(react-dom@16.14.0)(react@16.14.0)
13521 13596 rc-util: 5.38.0(react-dom@16.14.0)(react@16.14.0)
... ... @@ -13860,7 +13935,7 @@ packages:
13860 13935 react: '*'
13861 13936 react-dom: '*'
13862 13937 dependencies:
13863   - '@babel/runtime': 7.23.2
  13938 + '@babel/runtime': 7.23.8
13864 13939 classnames: 2.3.2
13865 13940 rc-resize-observer: 1.4.0(react-dom@16.14.0)(react@16.14.0)
13866 13941 rc-util: 5.38.0(react-dom@16.14.0)(react@16.14.0)
... ... @@ -13908,6 +13983,25 @@ packages:
13908 13983 react: 16.14.0
13909 13984 dev: false
13910 13985  
  13986 + /react-d3-tree@3.6.2(react-dom@16.14.0)(react@16.14.0):
  13987 + resolution: {integrity: sha512-1ExQlmEnv5iOw9XfZ3EcESDjzGXVKPAmyDJTJbvVfiwkplZtP7CcNEY0tKZf4XSW0FzYJf4aFXprGJen+95yuw==}
  13988 + peerDependencies:
  13989 + react: 16.x || 17.x || 18.x
  13990 + react-dom: 16.x || 17.x || 18.x
  13991 + dependencies:
  13992 + '@bkrem/react-transition-group': 1.3.3(react-dom@16.14.0)(react@16.14.0)
  13993 + '@types/d3-hierarchy': 1.1.11
  13994 + clone: 2.1.2
  13995 + d3-hierarchy: 1.1.9
  13996 + d3-selection: 3.0.0
  13997 + d3-shape: 1.3.7
  13998 + d3-zoom: 3.0.0
  13999 + dequal: 2.0.3
  14000 + react: 16.14.0
  14001 + react-dom: 16.14.0(react@16.14.0)
  14002 + uuid: 8.3.2
  14003 + dev: false
  14004 +
13911 14005 /react-dev-utils@9.0.1(eslint@8.52.0)(typescript@5.3.3)(webpack@5.89.0):
13912 14006 resolution: {integrity: sha512-pnaeMo/Pxel8aZpxk1WwxT3uXxM3tEwYvsjCYn5R7gNxjhN1auowdcLDzFB8kr7rafAj2rxmvfic/fbac5CzwQ==}
13913 14007 engines: {node: '>=8.10'}
... ... @@ -14073,7 +14167,7 @@ packages:
14073 14167 react: '>=15'
14074 14168 react-router: '>=5'
14075 14169 dependencies:
14076   - '@babel/runtime': 7.23.2
  14170 + '@babel/runtime': 7.23.8
14077 14171 react: 16.14.0
14078 14172 react-router: 5.2.0(react@16.14.0)
14079 14173  
... ... @@ -14083,7 +14177,7 @@ packages:
14083 14177 react: '>=15'
14084 14178 react-router: '>=5'
14085 14179 dependencies:
14086   - '@babel/runtime': 7.23.2
  14180 + '@babel/runtime': 7.23.8
14087 14181 react: 16.14.0
14088 14182 react-router: 5.3.4(react@16.14.0)
14089 14183  
... ... @@ -14092,7 +14186,7 @@ packages:
14092 14186 peerDependencies:
14093 14187 react: '>=15'
14094 14188 dependencies:
14095   - '@babel/runtime': 7.23.2
  14189 + '@babel/runtime': 7.23.8
14096 14190 history: 4.10.1
14097 14191 loose-envify: 1.4.0
14098 14192 prop-types: 15.8.1
... ... @@ -14106,7 +14200,7 @@ packages:
14106 14200 peerDependencies:
14107 14201 react: '>=15'
14108 14202 dependencies:
14109   - '@babel/runtime': 7.23.2
  14203 + '@babel/runtime': 7.23.8
14110 14204 history: 4.10.1
14111 14205 loose-envify: 1.4.0
14112 14206 prop-types: 15.8.1
... ... @@ -14121,7 +14215,7 @@ packages:
14121 14215 peerDependencies:
14122 14216 react: '>=15'
14123 14217 dependencies:
14124   - '@babel/runtime': 7.23.2
  14218 + '@babel/runtime': 7.23.8
14125 14219 history: 4.10.1
14126 14220 hoist-non-react-statics: 3.3.2
14127 14221 loose-envify: 1.4.0
... ... @@ -14138,7 +14232,7 @@ packages:
14138 14232 peerDependencies:
14139 14233 react: '>=15'
14140 14234 dependencies:
14141   - '@babel/runtime': 7.23.2
  14235 + '@babel/runtime': 7.23.8
14142 14236 history: 4.10.1
14143 14237 hoist-non-react-statics: 3.3.2
14144 14238 loose-envify: 1.4.0
... ... @@ -14405,7 +14499,7 @@ packages:
14405 14499 /regenerator-transform@0.15.2:
14406 14500 resolution: {integrity: sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==}
14407 14501 dependencies:
14408   - '@babel/runtime': 7.23.2
  14502 + '@babel/runtime': 7.23.8
14409 14503 dev: true
14410 14504  
14411 14505 /regex-not@1.0.2:
... ... @@ -17085,8 +17179,6 @@ packages:
17085 17179 resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==}
17086 17180 hasBin: true
17087 17181 requiresBuild: true
17088   - dev: true
17089   - optional: true
17090 17182  
17091 17183 /v8-compile-cache@2.3.0:
17092 17184 resolution: {integrity: sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==}
... ...
src/components/CarTableTreeAuth/index.tsx
... ... @@ -267,13 +267,8 @@ export default function Index({ onChange, value, disabled, brandMultiple = true,
267 267 render={(_, record: any, index) => {
268 268 return (
269 269 <Space>
270   - <Button
271   - type={record.brandId ? 'primary' : 'link'}
272   - style={{ padding: 2 }}
273   - onClick={() => onSelectSpec(record)}
274   - disabled={disabled}
275   - >
276   - {record.brandId ? '编辑车系' : record.seriesId ? '编辑车型' : ''}
  270 + <Button type="link" style={{ padding: 2 }} onClick={() => onSelectSpec(record)} disabled={disabled}>
  271 + {record.brandId ? '选择车系' : record.seriesId ? '选择车型' : ''}
277 272 </Button>
278 273 {/* <Popconfirm
279 274 title="确定删除?"
... ... @@ -296,8 +291,8 @@ export default function Index({ onChange, value, disabled, brandMultiple = true,
296 291  
297 292 {/* 选择品牌和车系 */}
298 293 <Modal
299   - title="选择车辆信息"
300   - visible={visible}
  294 + title={level === 2 ? '选择车系' : level === 3 ? '选择车型' : '选择品牌'}
  295 + open={visible}
301 296 onOk={() => form.submit()}
302 297 onCancel={() => {
303 298 setVisible(false);
... ...
src/components/Condition/CascaderSeries/SpecSelect.tsx
... ... @@ -4,13 +4,14 @@ import { CascaderOptionType } from &#39;antd/lib/cascader&#39;;
4 4 import { getBrand, getSeriesApi, getSpecList, optionItem } from './api';
5 5  
6 6 interface FilterProps {
7   - value?: { id: number, name: string }[],
8   - onChange?: (value: { id: number, name: string, energyType?: number }[]) => any,
9   - style?: any,
  7 + value?: { id: number; name: string }[];
  8 + onChange?: (value: { id: number; name: string; energyType?: number }[]) => any;
  9 + style?: any;
  10 + placeHolder?: string;
10 11 }
11 12  
12 13 export default function Filter(props: FilterProps) {
13   - const { value = [], onChange, style } = props;
  14 + const { value = [], onChange, style, placeHolder = '品牌/车系/车型' } = props;
14 15 const [specOptions, setSpecOptions] = useState<any[]>([]);
15 16  
16 17 useEffect(() => {
... ... @@ -19,12 +20,12 @@ export default function Filter(props: FilterProps) {
19 20  
20 21 function _brand() {
21 22 getBrand()
22   - .then(res => {
  23 + .then((res) => {
23 24 const { data = [] } = res;
24   - const brandList = data.map(brand => ({ ...brand, isLeaf: false }));
  25 + const brandList = data.map((brand) => ({ ...brand, isLeaf: false }));
25 26 setSpecOptions(brandList);
26 27 })
27   - .catch(e => {
  28 + .catch((e) => {
28 29 message.error(e.message);
29 30 });
30 31 }
... ... @@ -33,56 +34,61 @@ export default function Filter(props: FilterProps) {
33 34 if (specOptions && specOptions.length > 0) {
34 35 return options(specOptions);
35 36 }
36   - getBrand().then(res => {
  37 + getBrand().then((res) => {
37 38 const { data = [] } = res;
38   - const brandList = data.map(brand => ({ ...brand, isLeaf: false }));
  39 + const brandList = data.map((brand) => ({ ...brand, isLeaf: false }));
39 40 options(brandList);
40   - })
  41 + });
41 42 }
42 43  
43 44 function options(brandList: optionItem[] = []) {
44   - brandList && brandList.forEach(brand => {
45   - if (brand.id == value[0].id) {
46   - brand.children = [];
47   - getSeriesApi(value[0].id).then(res => {
48   - const { data = [] } = res;
49   - data.forEach((series, index) => {
50   - if (series.id === value[1].id) {
51   - getSpecList(value[1].id).then(res => {
52   - const { data = [] } = res;
53   - series.children = [];
54   - data.forEach((item) => {
55   - series.children.push({
56   - id: item.id,
57   - name: item.name,
58   - isLeaf: true,
59   - children: []
  45 + brandList &&
  46 + brandList.forEach((brand) => {
  47 + if (brand.id == value[0].id) {
  48 + brand.children = [];
  49 + getSeriesApi(value[0].id)
  50 + .then((res) => {
  51 + const { data = [] } = res;
  52 + data.forEach((series, index) => {
  53 + if (series.id === value[1].id) {
  54 + getSpecList(value[1].id)
  55 + .then((res) => {
  56 + const { data = [] } = res;
  57 + series.children = [];
  58 + data.forEach((item) => {
  59 + series.children.push({
  60 + id: item.id,
  61 + name: item.name,
  62 + isLeaf: true,
  63 + children: [],
  64 + });
  65 + });
  66 + brand.children.push({
  67 + id: series.id,
  68 + name: series.name,
  69 + isLeaf: false,
  70 + children: series.children,
  71 + });
  72 + setSpecOptions(brandList);
  73 + })
  74 + .catch((e) => {
  75 + message.error(e.message);
  76 + });
  77 + } else {
  78 + brand.children.push({
  79 + id: series.id,
  80 + name: series.name,
  81 + isLeaf: false,
  82 + children: series.children,
60 83 });
61   - });
62   - brand.children.push({
63   - id: series.id,
64   - name: series.name,
65   - isLeaf: false,
66   - children: series.children
67   - });
68   - setSpecOptions(brandList);
69   - }).catch(e => {
70   - message.error(e.message);
71   - })
72   - } else {
73   - brand.children.push({
74   - id: series.id,
75   - name: series.name,
76   - isLeaf: false,
77   - children: series.children
  84 + }
78 85 });
79   - }
80   - });
81   - }).catch(e => {
82   - message.error(e.message);
83   - })
84   - }
85   - })
  86 + })
  87 + .catch((e) => {
  88 + message.error(e.message);
  89 + });
  90 + }
  91 + });
86 92 }
87 93  
88 94 function loadData(selectedOptions: CascaderOptionType) {
... ... @@ -90,45 +96,47 @@ export default function Filter(props: FilterProps) {
90 96 const targetOption = selectedOptions[length - 1] || {};
91 97 targetOption.loading = true;
92 98 if (length === 1) {
93   - targetOption.id && getSeriesApi(targetOption.id)
94   - .then(res => {
95   - const { data = [] } = res;
96   - targetOption.loading = false;
97   - targetOption.children = [];
98   - data.forEach((list, index) => {
99   - targetOption.children.push({
100   - id: list.id,
101   - name: list.name,
102   - isLeaf: false,
  99 + targetOption.id &&
  100 + getSeriesApi(targetOption.id)
  101 + .then((res) => {
  102 + const { data = [] } = res;
  103 + targetOption.loading = false;
  104 + targetOption.children = [];
  105 + data.forEach((list, index) => {
  106 + targetOption.children.push({
  107 + id: list.id,
  108 + name: list.name,
  109 + isLeaf: false,
  110 + });
103 111 });
  112 + setSpecOptions([...specOptions]);
  113 + })
  114 + .catch((e) => {
  115 + message.error(e.message);
104 116 });
105   - setSpecOptions([...specOptions]);
106   - })
107   - .catch(e => {
108   - message.error(e.message);
109   - });
110 117 targetOption.loading = false;
111 118 }
112 119 if (length === 2) {
113   - targetOption.id && getSpecList(targetOption.id)
114   - .then(res => {
115   - const { data = [] } = res;
116   - targetOption.loading = false;
117   - targetOption.children = [];
118   - data.forEach((list, index) => {
119   - targetOption.children.push({
120   - id: list.id,
121   - name: list.name,
122   - isLeaf: true,
123   - /**能源类型 */
124   - energyType: list.energyType || false,
  120 + targetOption.id &&
  121 + getSpecList(targetOption.id)
  122 + .then((res) => {
  123 + const { data = [] } = res;
  124 + targetOption.loading = false;
  125 + targetOption.children = [];
  126 + data.forEach((list, index) => {
  127 + targetOption.children.push({
  128 + id: list.id,
  129 + name: list.name,
  130 + isLeaf: true,
  131 + /**能源类型 */
  132 + energyType: list.energyType || false,
  133 + });
125 134 });
  135 + setSpecOptions([...specOptions]);
  136 + })
  137 + .catch((e) => {
  138 + message.error(e.message);
126 139 });
127   - setSpecOptions([...specOptions]);
128   - })
129   - .catch(e => {
130   - message.error(e.message);
131   - });
132 140 }
133 141 }
134 142  
... ... @@ -147,8 +155,8 @@ export default function Filter(props: FilterProps) {
147 155 <Cascader
148 156 allowClear
149 157 changeOnSelect
150   - value={value.map(item => item.id)}
151   - placeholder="品牌/车系/车型"
  158 + value={value.map((item) => item.id)}
  159 + placeholder={placeHolder}
152 160 options={specOptions}
153 161 loadData={(selectedOptions: any) => loadData(selectedOptions)}
154 162 onChange={_onChange}
... ...
src/pages/carinsur/InsuranceTradingGift/api.ts
1 1 import type { http } from '@/typing/http';
2 2 import request from '@/utils/request';
3   -import qs from 'qs';
4   -import { ANGEL_Host, OOP_HOST } from '@/utils/host';
  3 +import { ANGEL_Host } from '@/utils/host';
  4 +import type { ValueNameOption } from '../entity';
5 5  
6 6 export interface TradingGiftConfig {
7   - conditions?: { label: string; value: string }[] | string[]; //前置条件
8   - exchangeMethod?: string[]; //礼品兑换方式
9   - vciReturnMoneyRate?: number; //商业险返现比例
10   - daiReturnMoneyRate?: number; //驾意险返现比例
11   - couponConfigList?: CouponResult[];
12   - key?: number;
  7 + conditions?: ValueNameOption[]; // 不传参到后端
  8 + vciReturnMoneyRate?: number; // 返现比例
  9 + cashCouponRate?: number; // 返现金券比例
  10 + noCashBackVciPremiumAmount?: number;
13 11 }
14 12  
15 13 export interface CouponResult {
... ... @@ -55,15 +53,14 @@ export interface PageItem {
55 53 buyInsuranceGiftConfigId: number; //配置id
56 54 insurerName?: number; //保险公司名称
57 55 type?: number; //成交类型,1新保、3续保(See: 保单类型枚举)
58   - shopsType?: number; //适用门店类型 1.表示全部门店 2.表示部分门店
  56 + shopsType?: number; //适用门店类型 1.表示全部门店 2.表示适用门店
59 57 shops?: Shops[]; //适用门店
60   - startDate?: number; //生效日期
61   - endDate?: number; //到期日期
62 58 brandName?: string; //品牌
63   - status?: number; //状态,1草稿、2审批中、3审批拒绝、4审批超时、5待生效、6生效中、7已失效
64   - approvalOrderNo?: string; // 审批单号
  59 + status?: number; //状态,1草稿、2审批中、3审批拒绝、4审批超时、5待生效、6生效中、7已失效 // 审批状态废弃
  60 + approvalOrderNo?: string; // 审批单号 // 审批状态废弃
65 61 applyCarList?: ApplyCarList[]; //适用车辆
66 62 insurers?: { insurerId: number; insurerName: string }[]; //保险公司集合
  63 + giftOptionList?: TradingGiftConfig[]; // 成交有礼条件配置
67 64 }
68 65 export interface Shops {
69 66 shopId?: number; //门店id
... ... @@ -97,7 +94,7 @@ export interface DetailData {
97 94 insurerId?: number; //保险公司id
98 95 insurerName?: string; //保险公司名称
99 96 type?: number; //成交类型,1新保、3续保(See: 保单类型枚举)
100   - shopsType?: number; //适用门店类型 1.表示全部门店 2.表示部分门店
  97 + shopsType?: number; //适用门店类型 1.表示全部门店 2.表示适用门店
101 98 startDate?: number; //生效日期
102 99 endDate?: number; //到期日期
103 100 shops?: Shops[];
... ... @@ -127,7 +124,7 @@ export function getTradingGiftPage(params?: PageParams): http.PromisePageResp&lt;Pa
127 124 /**
128 125 * 成交有礼前置条件
129 126 */
130   -export function getPreconditionList(params: any): http.PromiseResp<PreconditionItem[]> {
  127 +export function getPreconditionList(params: { type?: number }): http.PromiseResp<PreconditionItem[]> {
131 128 return request.get(`${ANGEL_Host}/buyinsurancegiftconfig/giftConditions`, { params });
132 129 }
133 130 /**
... ... @@ -172,8 +169,3 @@ export interface TerminateGiftConfigReq {
172 169 export function terminateGiftConfig(params: TerminateGiftConfigReq): http.PromiseResp<boolean> {
173 170 return request.post(`${ANGEL_Host}/buyinsurancegiftconfig/terminate/early`, { ...params });
174 171 }
175   -
176   -/**成交有礼提交审批*/
177   -export function applyGiftConfig(params: Data): http.PromiseResp<null> {
178   - return request.post(`${ANGEL_Host}/buyinsurancegiftconfig/apply`, { ...params });
179   -}
... ...
src/pages/carinsur/InsuranceTradingGift/components/AddModal.tsx
1   -import React, { useState, useEffect } from 'react';
2   -import { Modal, Form, Spin, Select, Radio, DatePicker, message } from 'antd';
  1 +import React, { useState, useEffect, useMemo } from 'react';
  2 +import { Modal, Form, Spin, Select, Radio, message, InputNumber, Row, Col } from 'antd';
3 3 import CarTableTreeAuth from '@/components/CarTableTreeAuth';
4   -import { CarAuthList } from '@/components/CarTableTreeAuth/entity';
5 4 import { PlusSquareOutlined } from '@ant-design/icons';
6   -import TradingGiftConfig from './TradingGiftConfig';
7   -import { typeList, carListFilter, businessCarListFilter, businessFilterGiftOption, filterGiftOption } from '../entity';
  5 +import { typeList, carListFilter, businessCarListFilter } from '../entity';
8 6 import useInitail from '@/hooks/useInitail';
9 7 import { useStore } from '../index';
10 8 import * as API from '../api';
11   -import moment from 'moment';
12 9 import _ from 'lodash';
  10 +import { useRequest } from 'umi';
  11 +import type { LabeledValue } from 'antd/lib/select';
  12 +import { BtnTypeEnum } from '../store';
  13 +import ShopSelectNew from '@/components/ShopSelectNew';
13 14  
14 15 export default function AddModal() {
15 16 const {
16 17 insurerList,
17   - shopsList,
18 18 addVisible,
19 19 buyInsuranceGiftConfigId,
20   - readOnly,
21   - setReadOnly,
22   - type,
23   - setType,
24 20 setBuyInsuranceGiftConfigId,
25 21 setAddVisible,
26 22 setLoading: setPageLoading,
27   - tradingGiftConfigList,
28   - setTradingGiftConfigList,
  23 + btnType,
29 24 } = useStore();
30 25 const [form] = Form.useForm();
31   - const [value, setValue] = useState<number>(1);
  26 +
32 27 const [delay, setDelay] = useState<boolean>(true);
33 28 const [loading, setLoading] = useState<boolean>(false);
34   - const { data: detailData, setData: setDetailData, setParams } = useInitail(API.getTradingGiftDetail, {}, {}, delay);
  29 + const { data: detailData, setParams, loading: detailLoading } = useInitail(API.getTradingGiftDetail, {}, {}, delay);
35 30  
36   - useEffect(() => {
37   - if (buyInsuranceGiftConfigId) {
38   - setParams({ buyInsuranceGiftConfigId }, true);
39   - setDelay(false);
40   - }
41   - }, [buyInsuranceGiftConfigId]);
  31 + const getPreconditionListHook = useRequest(API.getPreconditionList, {
  32 + throwOnError: true,
  33 + manual: true,
  34 + });
42 35  
43 36 useEffect(() => {
44   - if (Object.keys(detailData).length) {
45   - const { insurers, type, shopsType, shops, applyCarList, giftOptionList, startDate, endDate } = detailData;
46   - const insurerIds = insurers?.map((it) => it.insurerId);
47   - setValue(shopsType || 1);
48   - form.setFieldsValue({
49   - insurer: insurerList.filter((i) => insurerIds?.includes(i.id || 0)).map((it) => ({ value: it.id, label: it.name })),
50   - type: typeList.filter((i) => i.value === type)[0],
51   - shopsType,
52   - shops: shops?.length ? shops?.map((i) => ({ value: i.shopId, label: i.shopName })) : undefined,
53   - applyCarList: applyCarList?.length ? businessCarListFilter(applyCarList) : undefined,
54   - giftOptionList: giftOptionList?.length ? businessFilterGiftOption(giftOptionList) : undefined,
55   - time: startDate && endDate && [moment(startDate), moment(endDate)],
56   - });
57   - setType(type);
58   - setTradingGiftConfigList(businessFilterGiftOption(giftOptionList || []));
  37 + if (addVisible) {
  38 + if (buyInsuranceGiftConfigId && Object.keys(detailData).length) {
  39 + const { insurers, type, shopsType, shops, applyCarList, giftOptionList } = detailData;
  40 + const insurerIds = insurers?.map((it) => it.insurerId);
  41 + getPreconditionListHook.run({ type });
  42 + let initialValues = {
  43 + insurer: insurerList.filter((i) => insurerIds?.includes(i.id || 0)).map((it) => ({ value: it.id, label: it.name })),
  44 + type: typeList.filter((i) => i.value === type)[0],
  45 + shopsType,
  46 + shops: shops ? shops.map((i) => ({ value: i.shopId, label: i.shopName })) : undefined,
  47 + applyCarList: applyCarList ? businessCarListFilter(applyCarList) : undefined,
  48 + } as any;
  49 + if (giftOptionList && giftOptionList.length > 0) {
  50 + const { noCashBackVciPremiumAmount, vciReturnMoneyRate, cashCouponRate } = giftOptionList[0];
  51 + initialValues = {
  52 + ...initialValues,
  53 + noCashBackVciPremiumAmount,
  54 + vciReturnMoneyRate: vciReturnMoneyRate ? Number((vciReturnMoneyRate * 100).toFixed(4)) : undefined,
  55 + cashCouponRate: cashCouponRate ? Number((cashCouponRate * 100).toFixed(4)) : undefined,
  56 + };
  57 + }
  58 + form.setFieldsValue(initialValues);
  59 + } else {
  60 + getPreconditionListHook.run({ type: 1 });
  61 + }
  62 + } else {
  63 + form.resetFields();
59 64 }
60   - }, [detailData]);
  65 + }, [addVisible, detailData, buyInsuranceGiftConfigId]);
61 66  
62 67 useEffect(() => {
63   - if (value === 1) {
64   - form.resetFields(['shops']);
  68 + if (buyInsuranceGiftConfigId) {
  69 + setParams({ buyInsuranceGiftConfigId }, true);
  70 + setDelay(false);
65 71 }
66   - }, [value]);
  72 + }, [buyInsuranceGiftConfigId]);
67 73  
68 74 function handleSave(feildValue: any) {
69   - const { insurer, type, shops, shopsType, time, giftOptionList, applyCarList } = feildValue;
  75 + const { insurer, type, shops, shopsType, applyCarList, noCashBackVciPremiumAmount, vciReturnMoneyRate, cashCouponRate } = feildValue;
70 76 const params = {
71 77 buyInsuranceGiftConfigId,
72   - insurerIds: insurer?.length && insurer.map((i) => i.value),
  78 + insurerIds: insurer ? insurer.map((it: LabeledValue) => it.value) : undefined,
73 79 type: type?.value,
74 80 shopsType,
75   - shops: shops?.length && shops.map((it) => ({ shopId: it.value, shopName: it.label })),
76   - startDate: time[0] && moment(time[0]).valueOf(),
77   - endDate: time[1] && moment(time[1]).valueOf(),
78   - giftOptionList: giftOptionList.length && filterGiftOption(giftOptionList),
79   - applyCar: carListFilter(_.cloneDeep(applyCarList))[0],
  81 + shops: shops ? shops.map((it: LabeledValue) => ({ shopId: it.value, shopName: it.label })) : undefined,
  82 + giftOptionList: [
  83 + {
  84 + noCashBackVciPremiumAmount,
  85 + vciReturnMoneyRate: Number((vciReturnMoneyRate / 100).toFixed(4)),
  86 + cashCouponRate: Number((cashCouponRate / 100).toFixed(4)),
  87 + },
  88 + ],
  89 + applyCar: applyCarList ? carListFilter(_.cloneDeep(applyCarList))[0] : undefined,
80 90 };
  91 + if (btnType === BtnTypeEnum.复制) {
  92 + delete params.buyInsuranceGiftConfigId;
  93 + }
81 94 setLoading(true);
82 95 API.AddTradingGift({ ...params })
83 96 .then((res) => {
... ... @@ -91,50 +104,52 @@ export default function AddModal() {
91 104 setLoading(false);
92 105 });
93 106 }
  107 +
  108 + const tip = useMemo(() => {
  109 + if (getPreconditionListHook.data && getPreconditionListHook.data.length > 0) {
  110 + const tipObj = getPreconditionListHook.data.find((i) => i.name?.includes('商业险保费'));
  111 + return tipObj ? tipObj.name : '';
  112 + }
  113 + return '';
  114 + }, [getPreconditionListHook.data]);
  115 +
94 116 return (
95 117 <Modal
96   - title={`${readOnly ? '查看' : Object.keys(detailData).length ? '编辑' : '新增'}成交有礼配置`}
  118 + title={`${BtnTypeEnum[btnType ?? 0]}成交有礼配置`}
97 119 open={addVisible}
98 120 maskClosable={false}
99 121 onCancel={() => setAddVisible(false)}
  122 + destroyOnClose
100 123 onOk={() => {
101   - if (readOnly) {
102   - setAddVisible(false);
103   - } else {
104   - form.submit();
105   - }
  124 + form.submit();
106 125 }}
107 126 afterClose={() => {
108 127 form.resetFields();
109   - setTradingGiftConfigList([]);
110 128 setBuyInsuranceGiftConfigId(undefined);
111   - setValue(1);
112   - setDetailData({});
113   - setDelay(false);
114   - setType(undefined);
115   - readOnly && setReadOnly(false);
116 129 }}
117 130 width="68%"
118 131 >
119   - <Spin spinning={loading}>
  132 + <Spin spinning={loading || (buyInsuranceGiftConfigId ? detailLoading : false)}>
120 133 <Form
121 134 form={form}
122 135 onFinish={handleSave}
123 136 wrapperCol={{ span: 20 }}
124 137 labelCol={{ span: 4 }}
125   - disabled={readOnly}
126 138 initialValues={{
127 139 shopsType: 1,
  140 + type: {
  141 + label: '新保',
  142 + value: 1,
  143 + }, // 默认新保
128 144 }}
129 145 >
130   - <Form.Item label="保险公司" name="insurer" rules={[{ required: true, message: '请选择保险公司' }]}>
  146 + <Form.Item label="保险公司" name="insurer" rules={[{ required: true, message: '请选择保险公司' }]}>
131 147 <Select
132 148 placeholder="请选择保险公司"
133 149 mode="multiple"
134 150 showSearch
135 151 optionFilterProp="children"
136 152 labelInValue
137   - onSelect={(it: any) => {}}
138 153 filterOption={(input, option: any) => option?.children.indexOf(input) >= 0}
139 154 >
140 155 {insurerList &&
... ... @@ -145,19 +160,19 @@ export default function AddModal() {
145 160 ))}
146 161 </Select>
147 162 </Form.Item>
148   - <Form.Item label="成交类型" name="type" rules={[{ required: true, message: '请选择成交类型' }]}>
  163 + <Form.Item label="成交类型" name="type" rules={[{ required: true, message: '请选择成交类型' }]}>
149 164 <Select
150 165 placeholder="请选择成交类型"
151 166 showSearch
152 167 optionFilterProp="children"
153 168 labelInValue
  169 + filterOption={(input, option: any) => option?.children.indexOf(input) >= 0}
154 170 onSelect={(it: any) => {
155   - setType(it.value);
156   - if (type !== it.value && tradingGiftConfigList.length) {
157   - setTradingGiftConfigList([]);
  171 + if (getPreconditionListHook.loading) {
  172 + getPreconditionListHook.cancel();
158 173 }
  174 + getPreconditionListHook.run({ type: it.value });
159 175 }}
160   - filterOption={(input, option: any) => option?.children.indexOf(input) >= 0}
161 176 >
162 177 {typeList &&
163 178 typeList.map((item: any) => (
... ... @@ -167,60 +182,122 @@ export default function AddModal() {
167 182 ))}
168 183 </Select>
169 184 </Form.Item>
170   - <Form.Item label="适用门店:" name="shopsType" rules={[{ required: true, message: '请选择适用门店' }]}>
171   - <Radio.Group onChange={({ target }) => setValue(target.value || 1)}>
  185 + <Form.Item label="适用门店" name="shopsType" rules={[{ required: true, message: '请选择适用门店' }]}>
  186 + <Radio.Group>
172 187 <Radio value={1}>全部门店</Radio>
173 188 <Radio value={2}>部分门店</Radio>
174 189 </Radio.Group>
175 190 </Form.Item>
176   - {value === 2 && (
177   - <Form.Item label="门店:" name="shops" rules={[{ required: false, message: '请选择门店' }]}>
178   - <Select
179   - placeholder="请选择门店或输入关键字筛选"
180   - showSearch
181   - mode="multiple"
182   - optionFilterProp="children"
183   - labelInValue
184   - onSelect={(it: any) => {}}
185   - filterOption={(input, option: any) => option?.children.indexOf(input) >= 0}
186   - >
187   - {shopsList &&
188   - shopsList.map((item: any) => (
189   - <Select.Option value={item.id} key={item.id}>
190   - {item.name}
191   - </Select.Option>
192   - ))}
193   - </Select>
194   - </Form.Item>
195   - )}
196 191 <Form.Item
197   - name="applyCarList"
198   - label="适用车辆:"
199   - rules={[{ required: true, message: '选择车辆' }]}
200   - tooltip={
201   - <span style={{ color: '#ccc', fontSize: 12 }}>
202   - 点击图标
203   - <PlusSquareOutlined />
204   - 展开详情
205   - </span>
206   - }
  192 + noStyle
  193 + shouldUpdate={(prev, curr) => {
  194 + if (prev.shopsType !== curr.shopsType && curr.shopsType.value === 1) {
  195 + form.setFieldsValue({
  196 + shops: [],
  197 + });
  198 + }
  199 + return prev.shopsType !== curr.shopsType;
  200 + }}
207 201 >
208   - <CarTableTreeAuth brandMultiple={false} />
  202 + {({ getFieldValue }) => {
  203 + const shopsType = getFieldValue('shopsType');
  204 + if (shopsType !== 2) return null;
  205 + return (
  206 + <Row style={{ marginBottom: 24 }}>
  207 + <Col span={4} />
  208 + <Col span={20}>
  209 + <Form.Item label="" name="shops" rules={[{ required: true, message: '请选择门店' }]}>
  210 + <ShopSelectNew multiple destroyOnClose placeholder="请选择门店" />
  211 + </Form.Item>
  212 + </Col>
  213 + </Row>
  214 + );
  215 + }}
209 216 </Form.Item>
210   - <Form.Item name="time" label="签单日期:" rules={[{ required: true, message: '选择签单日期' }]}>
211   - <DatePicker.RangePicker style={{ width: '100%' }} />
  217 + <Form.Item
  218 + noStyle
  219 + shouldUpdate={(prev, curr) => {
  220 + return prev.type !== curr.type;
  221 + }}
  222 + >
  223 + {({ getFieldValue }) => {
  224 + const type = getFieldValue('type')?.value;
  225 + if (!type || type === 4) {
  226 + return null;
  227 + }
  228 + return (
  229 + <Form.Item
  230 + name="applyCarList"
  231 + label="适用车辆"
  232 + rules={[{ required: true, message: '选择车辆' }]}
  233 + tooltip={
  234 + <span style={{ color: '#ccc', fontSize: 12 }}>
  235 + 点击图标
  236 + <PlusSquareOutlined />
  237 + 展开详情
  238 + </span>
  239 + }
  240 + >
  241 + <CarTableTreeAuth brandMultiple={false} />
  242 + </Form.Item>
  243 + );
  244 + }}
  245 + </Form.Item>
  246 + <Form.Item
  247 + name="noCashBackVciPremiumAmount"
  248 + label="不享受成交有礼情况"
  249 + style={{ marginBottom: 0 }}
  250 + rules={[
  251 + {
  252 + required: true,
  253 + message: '请填写商业险保费',
  254 + },
  255 + ]}
  256 + >
  257 + <Row align="middle">
  258 + <span style={{ marginRight: 5 }}>{tip}</span>
  259 + <Form.Item name="noCashBackVciPremiumAmount" label="" style={{ marginBottom: 0 }}>
  260 + <InputNumber min={0.01} max={99999.99} placeholder="请输入" precision={2} addonAfter="元" />
  261 + </Form.Item>
  262 + </Row>
  263 + </Form.Item>
  264 + {getPreconditionListHook.data && getPreconditionListHook.data.length > 0 && (
  265 + <Row style={{ marginBottom: 24 }}>
  266 + <Col span={4} />
  267 + <Col span={20}>
  268 + {getPreconditionListHook.data.map((i) => {
  269 + return i.name?.includes('商业险保费') ? null : (
  270 + <div style={{ marginTop: 5 }} key={i.name}>
  271 + {i.name}
  272 + </div>
  273 + );
  274 + })}
  275 + </Col>
  276 + </Row>
  277 + )}
  278 + <Form.Item
  279 + name="vciReturnMoneyRate"
  280 + label="返现比例"
  281 + rules={[
  282 + {
  283 + required: true,
  284 + message: '请填写返现比例',
  285 + },
  286 + ]}
  287 + >
  288 + <InputNumber min={0.01} max={99.99} placeholder="请输入" precision={2} addonAfter="%" />
212 289 </Form.Item>
213 290 <Form.Item
214   - name="giftOptionList"
215   - label={
216   - <div>
217   - <span>成交有礼选项</span>
218   - </div>
219   - }
220   - rules={[{ required: true, message: '配置成交有礼条件' }]}
221   - tooltip={<span style={{ color: '#ccc', fontSize: 12 }}>客户从符合条件的选项中任选其一</span>}
  291 + name="cashCouponRate"
  292 + label="返现金券比例"
  293 + rules={[
  294 + {
  295 + required: true,
  296 + message: '请填写返现比例',
  297 + },
  298 + ]}
222 299 >
223   - <TradingGiftConfig />
  300 + <InputNumber min={0.01} max={99.99} placeholder="请输入" precision={2} addonAfter="%" />
224 301 </Form.Item>
225 302 </Form>
226 303 </Spin>
... ...
src/pages/carinsur/InsuranceTradingGift/components/AddTradingGiftModal.tsx deleted
1   -import React, { useState, useEffect } from 'react';
2   -import { Modal, Form, Select, InputNumber, message } from 'antd';
3   -import useInitail from '@/hooks/useInitail';
4   -import CouponConfig from './CouponConfig';
5   -import { useStore } from '../index';
6   -import * as API from '../api';
7   -
8   -export default function AddTradingGiftModal() {
9   - const {
10   - tradingGiftVisible,
11   - setTradingGiftVisible,
12   - couponList,
13   - setCouponList,
14   - tradingGiftConfigList,
15   - setTradingGiftConfigList,
16   - tradingGiftItem,
17   - setTradingGiftItem,
18   - type,
19   - readOnly,
20   - } = useStore();
21   - const [gift, setGift] = useState<string[]>([]);
22   - const [delay, setDelay] = useState<boolean>(true);
23   - const [form] = Form.useForm();
24   - const { data: preconditionList, setParams } = useInitail(API.getPreconditionList, [], { type }, delay);
25   - const { data: exchangeList } = useInitail(API.getExchangeList, [], {});
26   -
27   - useEffect(() => {
28   - if (type) {
29   - setParams({ type }, true);
30   - setDelay(false);
31   - }
32   - }, [type]);
33   -
34   - useEffect(() => {
35   - if (Object.keys(tradingGiftItem).length) {
36   - const { conditions, exchangeMethod, vciReturnMoneyRate, couponConfigList } = tradingGiftItem;
37   - form.setFieldsValue({
38   - conditions,
39   - exchangeMethod,
40   - vciReturnMoneyRate,
41   - couponConfigList,
42   - });
43   - setGift((exchangeMethod || ([] as string[])).map((i) => i.value));
44   - setCouponList(couponConfigList || []);
45   - }
46   - }, [tradingGiftItem]);
47   -
48   - useEffect(() => {
49   - if (gift.length < 2) {
50   - if (!gift.length) {
51   - setCouponList([]);
52   - form.resetFields(['vciReturnMoneyRate', 'discountCouponCodes']);
53   - return;
54   - }
55   - if (gift[0] === 'COUPON') {
56   - form.resetFields(['vciReturnMoneyRate']);
57   - } else {
58   - setCouponList([]);
59   - form.resetFields(['discountCouponCodes']);
60   - }
61   - }
62   - }, [gift]);
63   -
64   - function handleTrading(feildValue: any) {
65   - if (couponList?.length) {
66   - let noQuantitys: API.CouponResult[] = []; //未填数量
67   - let noValidityPeriodTypes: API.CouponResult[] = []; //有效期类型
68   - let noValidityPeriods: API.CouponResult[] = []; //未填有效期
69   - couponList.forEach((item: API.CouponResult) => {
70   - if (!item.quantity) {
71   - noQuantitys.push(item);
72   - } else if (!item.validityPeriodType) {
73   - noValidityPeriodTypes.push(item);
74   - } else if (item.validityPeriodType !== 1 && !item.validityPeriod) {
75   - noValidityPeriods.push(item);
76   - }
77   - });
78   - if (noQuantitys.length) {
79   - message.error(`请配置${noQuantitys[0].alias}优惠券的数量`);
80   - return;
81   - } else if (noValidityPeriodTypes.length) {
82   - message.error(`请配置${noValidityPeriodTypes[0].alias}优惠券的有效期类型`);
83   - return;
84   - } else if (noValidityPeriods.length) {
85   - message.error(`请配置${noValidityPeriods[0].alias}优惠券的有效期`);
86   - return;
87   - }
88   - }
89   - if (Object.keys(tradingGiftItem).length) {
90   - const { key } = tradingGiftItem;
91   - const _tradingGiftConfigList = tradingGiftConfigList.map((it) => {
92   - if (it.key === key) {
93   - return { ...feildValue, key };
94   - }
95   - return it;
96   - });
97   - setTradingGiftConfigList([..._tradingGiftConfigList]);
98   - } else {
99   - setTradingGiftConfigList([...tradingGiftConfigList, { ...feildValue, key: tradingGiftConfigList.length + 1 }]);
100   - }
101   - setTradingGiftItem({});
102   - setCouponList([]);
103   - form.resetFields();
104   - setTradingGiftVisible(false);
105   - }
106   - return (
107   - <Modal
108   - title={`${readOnly ? '查看' : Object.keys(tradingGiftItem).length ? '编辑' : '新增'}成交有礼选项`}
109   - open={tradingGiftVisible}
110   - onCancel={() => {
111   - setTradingGiftVisible(false);
112   - }}
113   - onOk={form.submit}
114   - afterClose={() => {
115   - form.resetFields();
116   - setCouponList([]);
117   - setGift([]);
118   - setTradingGiftItem({});
119   - }}
120   - width="60%"
121   - >
122   - <Form form={form} onFinish={handleTrading}>
123   - <Form.Item noStyle shouldUpdate>
124   - {({ getFieldValue }) => {
125   - const conditions: { value: string; label: string }[] = getFieldValue('conditions') || [];
126   - const _conditions = conditions.map((i) => i.value);
127   - const filterList = preconditionList.filter((i) => i.value && _conditions.includes(i.value)) || [];
128   - const valueList = filterList.map((i) => i.value);
129   - const batchNumberList = filterList.map((i) => i.batchNumber);
130   - return (
131   - <Form.Item label="前置条件:" name="conditions" rules={[{ required: true, message: '请选择前置条件' }]}>
132   - <Select
133   - placeholder="请选择前置条件"
134   - showSearch
135   - mode="multiple"
136   - optionFilterProp="children"
137   - labelInValue
138   - onSelect={(it: any) => {}}
139   - filterOption={(input, option: any) => option?.children.indexOf(input) >= 0}
140   - >
141   - {preconditionList &&
142   - preconditionList.map((item: API.PreconditionItem) => (
143   - <Select.Option
144   - disabled={batchNumberList.includes(item.batchNumber) && !valueList.includes(item.value)}
145   - value={item.value}
146   - key={item.value}
147   - >
148   - {item.name}
149   - </Select.Option>
150   - ))}
151   - </Select>
152   - </Form.Item>
153   - );
154   - }}
155   - </Form.Item>
156   - <Form.Item label="礼品兑换方式:" name="exchangeMethod" rules={[{ required: true, message: '请选择礼品兑换方式' }]}>
157   - <Select
158   - placeholder="请选择礼品兑换方式"
159   - showSearch
160   - mode="multiple"
161   - optionFilterProp="children"
162   - labelInValue
163   - onChange={(it) => {
164   - setGift(it.map((i) => i.value));
165   - }}
166   - onSelect={(it: any) => {}}
167   - filterOption={(input, option: any) => option?.children.indexOf(input) >= 0}
168   - >
169   - {exchangeList &&
170   - exchangeList.map((item: any) => (
171   - <Select.Option value={item.value} key={item.value}>
172   - {item.name}
173   - </Select.Option>
174   - ))}
175   - </Select>
176   - </Form.Item>
177   - {gift.includes('CASH_BACK') && (
178   - <>
179   - <Form.Item label="商业险返现比例:" name="vciReturnMoneyRate" rules={[{ required: true, message: '请填写续保商业险返现比例' }]}>
180   - <InputNumber placeholder="请输入" addonAfter="%" style={{ width: '50%' }} max={100} min={0} maxLength={5} precision={2} />
181   - </Form.Item>
182   - {/* <Form.Item label="驾乘险返现比例:" name="daiReturnMoneyRate" rules={[{ required: true, message: '请填写续保驾乘险返现比例' }]}>
183   - <InputNumber placeholder="请输入" addonAfter="%" style={{ width: '50%' }} max={100} min={0} maxLength={5} precision={2} />
184   - </Form.Item> */}
185   - </>
186   - )}
187   - {gift.includes('COUPON') && (
188   - <>
189   - <Form.Item label="优惠券:" name="couponConfigList" rules={[{ required: true, message: '请配置优惠券' }]}>
190   - <CouponConfig />
191   - </Form.Item>
192   - </>
193   - )}
194   - </Form>
195   - </Modal>
196   - );
197   -}
src/pages/carinsur/InsuranceTradingGift/components/ApprovalModal.tsx deleted
1   -/*
2   - * @Author: wangqiang@feewee.cn
3   - * @Date: 2022-10-26 09:24:59
4   - * @LastEditors: 谢忠泽 xiezhongze@feewee.cn
5   - * @LastEditTime: 2023-07-07 10:05:03
6   - */
7   -import ApprovalProgress from "@/components/ApprovalProgress";
8   -import { Button, Modal } from "antd";
9   -import React from "react";
10   -import { useStore } from "../index";
11   -
12   -export default function ApprovalProgressModal() {
13   - const { approvalProgressModalInfo, setApprovalProgressModalInfo } =
14   - useStore();
15   -
16   - const onCancel = () => setApprovalProgressModalInfo({ approvalOrderNo: '', visible: false });
17   -
18   - return (
19   - <Modal
20   - title={`${
21   - approvalProgressModalInfo.title
22   - ? approvalProgressModalInfo.title + "-"
23   - : ""
24   - }审批进度`}
25   - open={approvalProgressModalInfo.visible}
26   - onCancel={onCancel}
27   - maskClosable={false}
28   - destroyOnClose
29   - footer={[
30   - <Button key="cancel" onClick={onCancel}>
31   - 关闭
32   - </Button>,
33   - ]}
34   - >
35   - <ApprovalProgress orderNo={approvalProgressModalInfo.approvalOrderNo} />
36   - </Modal>
37   - );
38   -}
src/pages/carinsur/InsuranceTradingGift/components/CarModal.tsx
1   -import React, { useState, useEffect } from "react";
2   -import { Button, Modal, Table } from "antd";
  1 +import React, { useState, useEffect } from 'react';
  2 +import { Button, Modal } from 'antd';
3 3 import CarTableTreeAuth from '@/components/CarTableTreeAuth';
4 4 import { useStore } from '../index';
5 5 import { businessCarListFilter } from '../entity';
6   -import * as API from '../api';
7 6  
8 7 export default function CarModal() {
9   - const { carModalData, setCarModalData } = useStore();
10   - const { cars, visible } = carModalData;
11   - const [carData, setCarData] = useState<any[]>([]);
12   - useEffect(() => {
13   - if (cars?.length) {
14   - setCarData(businessCarListFilter(cars));
15   - }
16   - }, [cars]);
17   - return (
18   - <Modal
19   - title="适用车辆"
20   - width={600}
21   - open={visible}
22   - maskClosable={false}
23   - onCancel={() => setCarModalData({cars: [], visible: false})}
24   - footer={[
25   - <Button key="1" onClick={() => setCarModalData({cars: [], visible: false})}>取消</Button>,
26   - ]}
27   - >
28   - <CarTableTreeAuth value={carData} disabled brandMultiple={false} />
29   - </Modal>
30   - );
31   -}
32 8 \ No newline at end of file
  9 + const { carModalData, setCarModalData } = useStore();
  10 + const { cars, visible } = carModalData;
  11 + const [carData, setCarData] = useState<any[]>([]);
  12 + useEffect(() => {
  13 + if (cars?.length) {
  14 + setCarData(businessCarListFilter(cars));
  15 + }
  16 + }, [cars]);
  17 + return (
  18 + <Modal
  19 + title="适用车辆"
  20 + width={600}
  21 + open={visible}
  22 + maskClosable={false}
  23 + onCancel={() => setCarModalData({ cars: [], visible: false })}
  24 + destroyOnClose
  25 + footer={[
  26 + <Button key="1" onClick={() => setCarModalData({ cars: [], visible: false })}>
  27 + 取消
  28 + </Button>,
  29 + ]}
  30 + >
  31 + <CarTableTreeAuth value={carData} disabled brandMultiple={false} />
  32 + </Modal>
  33 + );
  34 +}
... ...
src/pages/carinsur/InsuranceTradingGift/components/CouponConfig.tsx deleted
1   -import React, { useState, useEffect } from 'react';
2   -import { Table, Select, Card, Button, Input, message, Divider, Popconfirm } from 'antd';
3   -import CouponConfigModal from '@/pages/coupon/CouponConfig';
4   -import { PlusOutlined } from '@ant-design/icons';
5   -import { useStore } from '../index';
6   -import { divide, cloneDeep } from 'lodash';
7   -import * as API from '../api';
8   -
9   -interface Props {
10   - onChange?: (data: API.CouponResult[]) => void;
11   - value?: API.CouponResult[];
12   -}
13   -
14   -const { Column } = Table;
15   -
16   -export default function CouponConfig({ onChange, value = [] }: Props) {
17   - const [row, setRow] = useState<API.CouponResult>({});
18   - const { readOnly, couponConfigVisible, setCouponConfigVisible, couponList, setCouponList, confNo, setConfNo } = useStore();
19   -
20   - function handleSaveCoupon(value: any) {
21   - const { classifyName, classifyCode, aliasName, amount, confNo } = value;
22   - const _couponList = cloneDeep(couponList);
23   - const nos = couponList.map((i) => i.no);
24   - let arr: API.CouponResult[] = [];
25   - if (!nos.includes(confNo)) {
26   - _couponList.push({ no: confNo, typeName: classifyName, typeNo: classifyCode, alias: aliasName, amount });
27   - arr = _couponList;
28   - } else {
29   - arr = _couponList.map((it) => {
30   - if (it.no === confNo) {
31   - const { quantity, validityPeriodType, validityPeriod, disabled } = row;
32   - return {
33   - no: confNo,
34   - typeName: classifyName,
35   - typeNo: classifyCode,
36   - alias: aliasName,
37   - amount,
38   - quantity,
39   - validityPeriodType,
40   - validityPeriod,
41   - disabled,
42   - };
43   - }
44   - return it;
45   - });
46   - setRow({});
47   - }
48   - setCouponList([...arr]);
49   - onChange && onChange([...arr]);
50   - setCouponConfigVisible(false);
51   - }
52   -
53   - /**
54   - * @description: 设置业务优惠卷配置
55   - * @param {*}
56   - * @return {*}
57   - */
58   - function handleCouponConfig(record: API.CouponResult, value: string, keyName: string) {
59   - const { no } = record;
60   - const _couponList = cloneDeep(couponList);
61   - let newList: API.CouponResult[] = [];
62   -
63   - if (!value) {
64   - newList = _couponList.map((item: API.CouponResult) => {
65   - if (item.no === no) {
66   - item[keyName] = null;
67   - }
68   - return item;
69   - });
70   - } else {
71   - const reg = /^[1-9]\d*$/;
72   - if (!reg.test(String(value))) {
73   - message.error('请填写正整数');
74   - return;
75   - }
76   -
77   - newList = _couponList.map((item: API.CouponResult) => {
78   - if (item.no === no) {
79   - if (value) {
80   - item[keyName] = Number(value);
81   - } else {
82   - item[keyName] = null;
83   - }
84   - }
85   - return item;
86   - });
87   - }
88   - setCouponList([...newList]);
89   - onChange && onChange([...newList]);
90   - }
91   -
92   - /**
93   - * @description: 选择有效期
94   - * @param {*}
95   - * @return {*}
96   - */
97   - function selectValidityPeriodType(record: API.CouponResult, value: any) {
98   - const _couponList = cloneDeep(couponList);
99   - const { no } = record;
100   - const newList = _couponList.map((item: API.CouponResult) => {
101   - if (item.no === no) {
102   - item.validityPeriodType = value;
103   - if (value === 1) {
104   - delete item.validityPeriod;
105   - item.disabled = true;
106   - } else {
107   - item.disabled = false;
108   - }
109   - }
110   - return item;
111   - });
112   - setCouponList([...newList]);
113   - onChange && onChange([...newList]);
114   - }
115   -
116   - /**
117   - * @description: 删除优惠卷配置
118   - * @param {API} item
119   - * @return {*}
120   - */
121   - async function _delete(item: API.CouponResult) {
122   - const { no = '' } = item;
123   - try {
124   - await API.delCoupon(no);
125   - const _couponList = cloneDeep(couponList);
126   - const filterList = _couponList.filter((it) => it.no !== no);
127   - setCouponList([...filterList]);
128   - onChange && onChange([...filterList]);
129   - } catch (error: any) {
130   - message.error(error.message);
131   - }
132   - }
133   - /**
134   - * @description: 编辑优惠卷配置
135   - * @param {API} item
136   - * @return {*}
137   - */
138   - function edit(item: API.CouponResult) {
139   - const { no = '' } = item;
140   - setRow({ ...item });
141   - setConfNo(no);
142   - setCouponConfigVisible(true);
143   - }
144   - return (
145   - <Card
146   - extra={
147   - <Button
148   - type="primary"
149   - icon={<PlusOutlined />}
150   - onClick={() => {
151   - setCouponConfigVisible(true);
152   - }}
153   - >
154   - 新增
155   - </Button>
156   - }
157   - >
158   - <Table dataSource={couponList || []} rowKey={(item) => `${item.no}`}>
159   - <Column title="类型" dataIndex="typeName" width={135} />
160   - <Column title="别名" dataIndex="alias" width={130} />
161   - <Column title="券面金额(元)" dataIndex="amount" width={120} />
162   - <Column
163   - title="数量(张)"
164   - dataIndex="quantity"
165   - width={120}
166   - render={(text, record: any) => (
167   - <Input value={record.quantity} onChange={(value) => handleCouponConfig(record, value.target.value, 'quantity')} placeholder="请输入" />
168   - )}
169   - />
170   - <Column
171   - title="有效期类型"
172   - dataIndex="validityPeriodType"
173   - width={120}
174   - render={(text, record: any) => (
175   - <Select placeholder="请选择" value={record.validityPeriodType} onChange={(value) => selectValidityPeriodType(record, value)}>
176   - <Select.Option key="1_1" value={1}>
177   - 当天有效
178   - </Select.Option>
179   - <Select.Option key="2_2" value={2}>
180   - 小时
181   - </Select.Option>
182   - <Select.Option key="3_3" value={3}>
183   - 天
184   - </Select.Option>
185   - </Select>
186   - )}
187   - />
188   - <Column
189   - title="有效期"
190   - dataIndex="validityPeriod"
191   - width={100}
192   - render={(text, record: any) => (
193   - <Input
194   - value={record.validityPeriod}
195   - disabled={record.disabled}
196   - onChange={(value) => handleCouponConfig(record, value.target.value, 'validityPeriod')}
197   - placeholder="请输入"
198   - />
199   - )}
200   - />
201   - <Column
202   - title="操作"
203   - align="center"
204   - width={120}
205   - render={(text, _item: API.CouponResult) => (
206   - <span>
207   - <a
208   - onClick={() => {
209   - edit(_item);
210   - }}
211   - style={{ marginLeft: 5 }}
212   - >
213   - {readOnly ? '查看' : '编辑'}
214   - </a>
215   - {!readOnly && (
216   - <>
217   - <Divider type="vertical" />
218   - <Popconfirm title="是否删除?" onConfirm={() => _delete(_item)} okText="确定" cancelText="取消">
219   - <a
220   - onClick={(e) => {
221   - e.preventDefault();
222   - }}
223   - style={{ color: 'red', marginLeft: 5 }}
224   - >
225   - 删除
226   - </a>
227   - </Popconfirm>
228   - </>
229   - )}
230   - </span>
231   - )}
232   - />
233   - </Table>
234   - <CouponConfigModal
235   - remark="成交有礼"
236   - confNo={confNo}
237   - visible={couponConfigVisible}
238   - onSave={(value: any) => {
239   - handleSaveCoupon(value);
240   - setConfNo('');
241   - }}
242   - onCancel={() => {
243   - setCouponConfigVisible(false);
244   - setConfNo('');
245   - setRow({});
246   - }}
247   - />
248   - </Card>
249   - );
250   -}
src/pages/carinsur/InsuranceTradingGift/components/Filter.tsx
1 1 import React, { useState } from 'react';
2   -import { Select, DatePicker } from 'antd';
3   -import { getShopApi } from '@/common/api';
4   -import { getInsurerList } from '../api';
5   -import useInitail from '@/hooks/useInitail';
  2 +import { Select } from 'antd';
6 3 import CascaderSpec from '@/components/Condition/CascaderSeries/SpecSelect';
7 4 import { useStore } from '../index';
8 5 import { debounce } from 'lodash';
9 6  
10 7 interface Props {
11   - onChange?:(params:{})=> any;
  8 + onChange?: (params: {}) => any;
12 9 }
13 10  
14   -function Filter({ onChange }:Props) {
15   - const { insurerList, shopsList } = useStore();
16   - const [value, setValue] = useState<{ id: number, name: string }[]>([]);
17   - const _onChange = debounce((params:any) => {
18   - onChange && onChange(params);
19   - }, 500);
20   - return (
21   - <div>
22   - <Select
23   - style={{ marginRight: 20, minWidth: 250 }}
24   - placeholder="请选择保险公司"
25   - showSearch
26   - optionFilterProp="children"
27   - allowClear
28   - onChange={(insurerId) => {
29   - _onChange({insurerId});
30   - }}
31   - >
32   - {
33   - insurerList && insurerList.map((item:any) => (
34   - <Select.Option value={item.id} key={item.id}>
35   - {item.name}
36   - </Select.Option>
37   - ))
38   - }
39   - </Select>
40   - <Select
41   - style={{ marginRight: 20, minWidth: 200 }}
42   - placeholder="请选择门店"
43   - showSearch
44   - optionFilterProp="children"
45   - allowClear
46   - onChange={(shopId) => {
47   - _onChange({shopId});
48   - }}
49   - >
50   - {
51   - shopsList && shopsList.map((item:any) => (
52   - <Select.Option value={item.id} key={item.id}>
53   - {item.name}
54   - </Select.Option>
55   - ))
56   - }
57   - </Select>
58   - <CascaderSpec
59   - style={{minWidth: 250}}
60   - value={value}
61   - onChange={(v) => {
62   - setValue([...v]);
63   - const params = { brandId: v[0]?.id, seriesId: v[1]?.id, specId: v[2]?.id };
64   - _onChange({...params});
65   - }}
66   - />
67   - </div>
68   - );
  11 +function Filter({ onChange }: Props) {
  12 + const { insurerList, shopsList } = useStore();
  13 + const [value, setValue] = useState<{ id: number; name: string }[]>([]);
  14 + const _onChange = debounce((params: any) => {
  15 + onChange && onChange(params);
  16 + }, 500);
  17 + return (
  18 + <div>
  19 + <Select
  20 + style={{ marginRight: 20, minWidth: 250 }}
  21 + placeholder="搜索保险公司"
  22 + showSearch
  23 + optionFilterProp="children"
  24 + allowClear
  25 + onChange={(insurerId) => {
  26 + _onChange({ insurerId });
  27 + }}
  28 + >
  29 + {insurerList &&
  30 + insurerList.map((item: any) => (
  31 + <Select.Option value={item.id} key={item.id}>
  32 + {item.name}
  33 + </Select.Option>
  34 + ))}
  35 + </Select>
  36 + <Select
  37 + style={{ marginRight: 20, minWidth: 200 }}
  38 + placeholder="搜索适用门店"
  39 + showSearch
  40 + optionFilterProp="children"
  41 + allowClear
  42 + onChange={(shopId) => {
  43 + _onChange({ shopId });
  44 + }}
  45 + >
  46 + {shopsList &&
  47 + shopsList.map((item: any) => (
  48 + <Select.Option value={item.id} key={item.id}>
  49 + {item.name}
  50 + </Select.Option>
  51 + ))}
  52 + </Select>
  53 + <CascaderSpec
  54 + placeHolder="搜索适用车辆"
  55 + style={{ minWidth: 250 }}
  56 + value={value}
  57 + onChange={(v) => {
  58 + setValue([...v]);
  59 + const params = { brandId: v[0]?.id, seriesId: v[1]?.id, specId: v[2]?.id };
  60 + _onChange({ ...params });
  61 + }}
  62 + />
  63 + </div>
  64 + );
69 65 }
70 66  
71 67 export default Filter;
... ...
src/pages/carinsur/InsuranceTradingGift/components/List.tsx
1   -import React from 'react';
2   -import { Table, Tag, Space, Popconfirm, Divider, message } from 'antd';
3   -import moment from 'moment';
  1 +import React, { useRef } from 'react';
  2 +import { Table, Space, Popconfirm, Divider, message } from 'antd';
4 3 import { useStore } from '../index';
5   -import { TypeEnum, StatusTextEnum, StatusStyleList, StatusColorList } from '../entity';
  4 +import { TypeEnum } from '../entity';
6 5 import * as API from '../api';
7 6 import TextWithMore from '@/components/TextWithMore';
  7 +import type { ApprovalProgressModalRef } from '@/components/ApprovalProgressModal';
  8 +import ApprovalProgressModal from '@/components/ApprovalProgressModal';
  9 +import { BtnTypeEnum } from '../store';
  10 +import _ from 'lodash';
  11 +import type { ValueNameOption } from '../../entity';
8 12  
9 13 const { Column } = Table;
10 14  
11   -interface Props {
12   - status: number;
13   -}
14   -
15   -export default function List({ status: radioStatus }: Props) {
16   - const {
17   - tradingGiftList,
18   - paginationConfig,
19   - loading,
20   - setLoading,
21   - setApprovalProgressModalInfo,
22   - setBuyInsuranceGiftConfigId,
23   - setShopsModalData,
24   - setCarModalData,
25   - setAddVisible,
26   - setReadOnly,
27   - setTerminateModalData,
28   - setCurrItem,
29   - } = useStore();
  15 +export default function List() {
  16 + const approvalProgressRef = useRef<ApprovalProgressModalRef | null>(null);
  17 + const { tradingGiftList, paginationConfig, loading, setLoading, setBuyInsuranceGiftConfigId, setCarModalData, setAddVisible, setBtnType } =
  18 + useStore();
30 19  
31 20 /**
32   - * @description: 查看
  21 + * @description: 编辑
33 22 * @param {number} buyInsuranceGiftConfigId
34 23 * @return {*}
35 24 */
36   - function handleReadOnly(buyInsuranceGiftConfigId: number) {
  25 + function handleEdit(buyInsuranceGiftConfigId: number) {
  26 + setBtnType(BtnTypeEnum.编辑);
37 27 setBuyInsuranceGiftConfigId(buyInsuranceGiftConfigId);
38   - setReadOnly(true);
39 28 setAddVisible(true);
40 29 }
41   -
42   - /**
43   - * @description: 编辑
44   - * @param {number} buyInsuranceGiftConfigId
45   - * @return {*}
46   - */
47   - function handleEdit(buyInsuranceGiftConfigId: number) {
  30 + // 复制
  31 + function handleCopy(buyInsuranceGiftConfigId: number) {
  32 + setBtnType(BtnTypeEnum.复制);
48 33 setBuyInsuranceGiftConfigId(buyInsuranceGiftConfigId);
49 34 setAddVisible(true);
50 35 }
... ... @@ -63,140 +48,84 @@ export default function List({ status: radioStatus }: Props) {
63 48 message.error(error.message);
64 49 }
65 50 }
66   - /**
67   - * @description: 撤销
68   - * @param {number} buyInsuranceGiftConfigId
69   - * @return {*}
70   - */
71   - async function handleRevoke(buyInsuranceGiftConfigId: number) {
72   - try {
73   - await API.revokeGiftConfig({ buyInsuranceGiftConfigId });
74   - message.success('撤销成功');
75   - setLoading(true);
76   - } catch (error: any) {
77   - message.error(error.message);
78   - }
79   - }
80   - /**
81   - * @description: 提审
82   - * @param {number} buyInsuranceGiftConfigId
83   - * @return {*}
84   - */
85   - async function handleApply(buyInsuranceGiftConfigId: number) {
86   - try {
87   - await API.applyGiftConfig({ buyInsuranceGiftConfigId });
88   - message.success('提交成功');
89   - setLoading(true);
90   - } catch (error: any) {
91   - message.error(error.message);
92   - }
93   - }
94 51  
95 52 /**
96 53 * @description: 操作集合
97   - * @param {number} status
98 54 * @param {number} buyInsuranceGiftConfigId
99 55 * @return {*}
100 56 */
101   - function operationDom(status: number = 0, buyInsuranceGiftConfigId: number, item: API.PageItem) {
  57 + function operationDom(buyInsuranceGiftConfigId: number) {
102 58 return (
103 59 <Space>
104   - <a onClick={() => handleReadOnly(buyInsuranceGiftConfigId)}>查看</a>
105   - {[1, 3, 4].includes(status) && (
106   - <>
107   - <Divider type="vertical" />
108   - <Popconfirm title="是否删除?" onConfirm={() => handleDelete(buyInsuranceGiftConfigId)} okText="确定" cancelText="取消">
109   - <a style={{ color: '#F8593E' }}>删除</a>
110   - </Popconfirm>
111   - </>
112   - )}
113   - {status === 2 && (
114   - <>
115   - <Divider type="vertical" />
116   - <Popconfirm title="是否撤销?" onConfirm={() => handleRevoke(buyInsuranceGiftConfigId)} okText="确定" cancelText="取消">
117   - <a style={{ color: '#FF794C' }}>撤销</a>
118   - </Popconfirm>
119   - </>
120   - )}
121   - {[1, 3, 4, 5, 6].includes(status) && (
122   - <>
123   - <Divider type="vertical" />
124   - <Popconfirm
125   - title={`是否${![5, 6].includes(status) ? '编辑' : '变更'}?`}
126   - onConfirm={() => handleEdit(buyInsuranceGiftConfigId)}
127   - okText="确定"
128   - cancelText="取消"
129   - >
130   - <a>{![5, 6].includes(status) ? '编辑' : '变更'}</a>
131   - </Popconfirm>
132   - </>
133   - )}
134   - {[1].includes(status) && (
135   - <>
136   - <Divider type="vertical" />
137   - <Popconfirm title="是否提交审批?" onConfirm={() => handleApply(buyInsuranceGiftConfigId)} okText="确定" cancelText="取消">
138   - <a>提交审批</a>
139   - </Popconfirm>
140   - </>
141   - )}
142   - {[6].includes(status) && (
143   - <>
144   - <Divider type="vertical" />
145   - <a
146   - onClick={() => {
147   - setCurrItem(item);
148   - setTerminateModalData({ date: moment(), visible: true });
149   - }}
150   - >
151   - 提前结束
152   - </a>
153   - </>
154   - )}
  60 + <Popconfirm title="是否删除?" onConfirm={() => handleDelete(buyInsuranceGiftConfigId)} okText="确定" cancelText="取消">
  61 + <a style={{ color: '#F8593E' }}>删除</a>
  62 + </Popconfirm>
  63 + <Divider type="vertical" />
  64 + <a onClick={() => handleEdit(buyInsuranceGiftConfigId)}>编辑</a>
  65 + <Divider type="vertical" />
  66 + <a style={{ color: '#F8593E' }} onClick={() => handleCopy(buyInsuranceGiftConfigId)}>
  67 + 复制
  68 + </a>
155 69 </Space>
156 70 );
157 71 }
158 72  
159 73 return (
160   - <Table dataSource={tradingGiftList || []} rowKey={(item) => item.buyInsuranceGiftConfigId} pagination={paginationConfig} loading={loading}>
161   - <Column title="保险公司" dataIndex="insurers" render={(t) => <TextWithMore title="保险公司" dataIndex="insurerName" list={t ?? []} />} />
162   - <Column title="成交类型" dataIndex="type" render={(t: number) => TypeEnum[t || 0]} />
163   - <Column
164   - title="适用门店"
165   - dataIndex="shops"
166   - render={(t) => {
167   - return t.length ? <a onClick={() => setShopsModalData({ shops: t, visible: true })}>查看</a> : <div>全部门店</div>;
168   - }}
169   - />
170   - <Column
171   - title="签单日期"
172   - dataIndex="type"
173   - render={(t, item: API.PageItem) => <div>{`${moment(item.startDate).format('YYYY.MM.DD')}~${moment(item.endDate).format('YYYY.MM.DD')}`}</div>}
174   - />
175   - <Column title="品牌" dataIndex="brandName" render={(t) => t || '--'} />
176   - <Column
177   - title="适用车辆"
178   - dataIndex="applyCarList"
179   - render={(t: API.ApplyCarList[]) => <a onClick={() => setCarModalData({ cars: t, visible: true })}>查看</a>}
180   - />
181   - <Column
182   - title="审批进度"
183   - dataIndex="approvalOrderNo"
184   - render={(t: string, item: API.PageItem) =>
185   - ![1].includes(item.status || 0) ? <a onClick={() => setApprovalProgressModalInfo({ approvalOrderNo: t, visible: true })}>查看</a> : <>--</>
186   - }
187   - />
188   - {radioStatus === 2 && (
  74 + <>
  75 + <Table dataSource={tradingGiftList || []} rowKey={(item) => item.buyInsuranceGiftConfigId} pagination={paginationConfig} loading={loading}>
  76 + <Column title="编号" dataIndex="buyInsuranceGiftConfigId" />
  77 + <Column title="保险公司" dataIndex="insurers" render={(t) => <TextWithMore title="保险公司" dataIndex="insurerName" list={t ?? []} />} />
  78 + <Column title="成交类型" dataIndex="type" render={(t: number) => TypeEnum[t || 0]} />
  79 + <Column
  80 + title="适用门店"
  81 + dataIndex="shops"
  82 + render={(t, record: API.PageItem) =>
  83 + record.shopsType === 1 ? '全部门店' : <TextWithMore title="适用门店" dataIndex="shopName" list={t ?? []} unit="个门店" />
  84 + }
  85 + />
  86 + <Column title="适用品牌" dataIndex="brandName" render={(t) => t || '--'} />
  87 + <Column
  88 + title="适用车辆"
  89 + dataIndex="applyCarList"
  90 + render={(t: API.ApplyCarList[]) => (t && t.length > 0 ? <a onClick={() => setCarModalData({ cars: t, visible: true })}>查看</a> : <>--</>)}
  91 + />
  92 + <Column
  93 + title="不享受成交有礼情况"
  94 + dataIndex="giftOptionList"
  95 + render={(t) => {
  96 + if (!_.isArray(t) || t.length === 0) {
  97 + return '--';
  98 + }
  99 + return t[0].conditions.map((i: ValueNameOption, index: number) => (
  100 + <div key={i.value}>
  101 + {index + 1}.{i.name}
  102 + </div>
  103 + ));
  104 + }}
  105 + />
  106 + <Column
  107 + title="返现比例"
  108 + dataIndex="giftOptionList"
  109 + render={(t) => {
  110 + if (!_.isArray(t) || t.length === 0) {
  111 + return '--';
  112 + }
  113 + return `${(t[0].vciReturnMoneyRate * 100).toFixed(2)}%`;
  114 + }}
  115 + />
189 116 <Column
190   - title="状态"
191   - dataIndex="status"
192   - render={(t: number) => (
193   - <Tag style={StatusStyleList[t || 0]} color={StatusColorList[t || 0]}>
194   - {StatusTextEnum[t || 0]}
195   - </Tag>
196   - )}
  117 + title="返现金券比例"
  118 + dataIndex="giftOptionList"
  119 + render={(t) => {
  120 + if (!_.isArray(t) || t.length === 0) {
  121 + return '--';
  122 + }
  123 + return `${(t[0].cashCouponRate * 100).toFixed(2)}%`;
  124 + }}
197 125 />
198   - )}
199   - <Column title="操作" render={(t: number, item: API.PageItem) => <>{operationDom(item.status || 0, item.buyInsuranceGiftConfigId, item)}</>} />
200   - </Table>
  126 + <Column title="操作" render={(t: number, item: API.PageItem) => <>{operationDom(item.buyInsuranceGiftConfigId)}</>} />
  127 + </Table>
  128 + <ApprovalProgressModal ref={approvalProgressRef} />
  129 + </>
201 130 );
202 131 }
... ...
src/pages/carinsur/InsuranceTradingGift/components/ShopModal.tsx deleted
1   -import React from "react";
2   -import { Button, Modal, Table } from "antd";
3   -import { useStore } from '../index';
4   -import * as API from '../api';
5   -
6   -const { Column } = Table;
7   -
8   -export default function ShopModal() {
9   - const { shopsModalData, setShopsModalData } = useStore();
10   - const { shops, visible } = shopsModalData;
11   - return (
12   - <Modal
13   - title="门店"
14   - width={600}
15   - open={visible}
16   - maskClosable={false}
17   - onCancel={() => setShopsModalData({shops: [], visible: false})}
18   - footer={[
19   - <Button key="1" onClick={() => setShopsModalData({shops: [], visible: false})}>取消</Button>,
20   - ]}
21   - >
22   - <Table
23   - size="small"
24   - dataSource={shops || []}
25   - scroll={{ y: 500 }}
26   - rowKey={(item: KtCanteenSpace.ServiceShop) => `${item.shopId}`}
27   - >
28   - <Column title="门店" dataIndex="shopName" width="100%" align="center" />
29   - </Table>
30   - </Modal>
31   - );
32   -}
33 0 \ No newline at end of file
src/pages/carinsur/InsuranceTradingGift/components/TerminateModal.tsx deleted
1   -import React from 'react';
2   -import { Calendar, Modal, message } from 'antd';
3   -import { useStore } from '../index';
4   -import moment from 'moment';
5   -import { terminateGiftConfig } from '../api';
6   -import styles from './style.less';
7   -
8   -export default function TerminateModal() {
9   - const { terminateModalData, setTerminateModalData, setParams, innerParams, currItem } = useStore();
10   -
11   - const handleTerminate = () => {
12   - if (!terminateModalData.date) {
13   - message.error('请选择签单结束日期');
14   - return;
15   - }
16   - Modal.confirm({
17   - title: '确认提交签单结束日期吗?',
18   - zIndex: 1002,
19   - onOk: async () => {
20   - const hide = message.loading('提交中,请稍候', 0);
21   - terminateGiftConfig({ buyInsuranceGiftConfigId: currItem?.buyInsuranceGiftConfigId, endDate: terminateModalData.date?.valueOf() })
22   - .then(() => {
23   - message.success('提交成功');
24   - setParams({ ...innerParams }, true);
25   - setTerminateModalData({ date: moment(), visible: false });
26   - })
27   - .catch((error: any) => {
28   - message.error(error.message ?? '请求失败');
29   - })
30   - .finally(() => {
31   - hide();
32   - });
33   - },
34   - });
35   - };
36   -
37   - return (
38   - <Modal
39   - title="选择签单结束日期"
40   - width={600}
41   - open={terminateModalData.visible}
42   - maskClosable={false}
43   - onCancel={() =>
44   - setTerminateModalData({
45   - date: moment(),
46   - visible: false,
47   - })
48   - }
49   - onOk={handleTerminate}
50   - destroyOnClose
51   - >
52   - <div className={styles.terminateCalendar}>
53   - <Calendar
54   - mode="month"
55   - value={terminateModalData.date}
56   - disabledDate={(currDate) => {
57   - return currDate.valueOf() > moment(currItem?.endDate).endOf('day').valueOf() || currDate.valueOf() < moment().startOf('day').valueOf();
58   - }}
59   - onChange={(value) => {
60   - setTerminateModalData({ ...terminateModalData, date: value });
61   - }}
62   - />
63   - </div>
64   - </Modal>
65   - );
66   -}
src/pages/carinsur/InsuranceTradingGift/components/TradingGiftConfig.tsx
1   -import React, { useState, useEffect, useRef } from 'react';
2   -import { Modal, Button, Form, Spin, Select, Radio, Table, Divider, Popconfirm } from 'antd';
3   -import { PlusOutlined } from '@ant-design/icons';
4   -import { typeList, carListFilter } from '../entity';
5   -import AddTradingGiftModal from './AddTradingGiftModal';
  1 +import React, { useRef, useContext, useEffect, useImperativeHandle, Ref, forwardRef } from 'react';
  2 +import { Form, Table, InputNumber } from 'antd';
6 3 import { useStore } from '../index';
7 4 import * as API from '../api';
8 5 import _ from 'lodash';
  6 +import type { InputRef } from 'antd';
  7 +import type { FormInstance } from 'antd/es/form';
9 8  
10   -const { Column } = Table;
  9 +// 此交互暂不使用
  10 +const EditableContext = React.createContext<FormInstance<any> | null>(null);
  11 +
  12 +interface EditableRowProps {
  13 + index: number;
  14 +}
  15 +
  16 +const EditableRow: React.FC<EditableRowProps> = ({ index, ...rest }) => {
  17 + return <tr {...rest} />;
  18 +};
  19 +
  20 +interface EditableCellProps {
  21 + title: React.ReactNode;
  22 + editable: boolean;
  23 + children: React.ReactNode;
  24 + dataIndex: keyof API.TradingGiftConfig;
  25 + record: API.TradingGiftConfig;
  26 + handleSave: (record: API.TradingGiftConfig) => void;
  27 +}
  28 +
  29 +const EditableCell: React.FC<EditableCellProps> = ({ title, editable, children, dataIndex, record, handleSave, ...restProps }) => {
  30 + const inputRef = useRef<InputRef>(null);
  31 + const form = useContext(EditableContext)!;
  32 +
  33 + // 回车或者失去焦点时保存输入框值
  34 + const save = async () => {
  35 + try {
  36 + const values = await form.validateFields();
  37 + handleSave({ ...record, ...values });
  38 + } catch (errInfo) {
  39 + console.log('Save failed:', errInfo);
  40 + }
  41 + };
  42 +
  43 + useEffect(() => {
  44 + // 编辑时详情数据为异步,设置 initialValue 无效,需手动给表单赋值
  45 + form.setFieldsValue({
  46 + vciReturnMoneyRate: record?.vciReturnMoneyRate,
  47 + cashCouponRate: record?.cashCouponRate,
  48 + });
  49 + }, [record]);
  50 +
  51 + const childNode = editable ? (
  52 + <Form.Item
  53 + name={dataIndex}
  54 + style={{ margin: 0 }}
  55 + rules={[
  56 + {
  57 + required: true,
  58 + message: `${title}值必须在 0.01-99.99 之间`,
  59 + },
  60 + ]}
  61 + >
  62 + {/* @ts-ignore */}
  63 + <InputNumber ref={inputRef} min={0.01} max={99.99} placeholder="请输入" precision={2} addonAfter="%" onPressEnter={save} onBlur={save} />
  64 + </Form.Item>
  65 + ) : (
  66 + children
  67 + );
  68 +
  69 + return <td {...restProps}>{childNode}</td>;
  70 +};
11 71  
12 72 interface Props {
13   - onChange?: (_: API.TradingGiftConfig[]) => any;
14 73 value?: API.TradingGiftConfig[];
  74 + onChange?: (options: API.TradingGiftConfig[]) => void;
15 75 }
16 76  
17   -export default function TradingGiftConfig({ onChange, value = [] }: Props) {
18   - const { readOnly, setTradingGiftVisible, tradingGiftConfigList, setTradingGiftConfigList, setTradingGiftItem } = useStore();
  77 +type EditableTableProps = Parameters<typeof Table>[0];
  78 +type ColumnTypes = Exclude<EditableTableProps['columns'], undefined>;
19 79  
20   - const update = useRef(false);
  80 +export interface TradingGiftConfigRef {
  81 + validate: () => void;
  82 +}
21 83  
22   - useEffect(() => {
23   - if (!update.current) {
24   - update.current = true;
25   - } else {
26   - onChange && onChange([...tradingGiftConfigList]);
  84 +function TradingGiftConfig({ onChange, value = [] }: Props, ref: Ref<TradingGiftConfigRef>) {
  85 + const { readOnly } = useStore();
  86 +
  87 + const [form] = Form.useForm();
  88 +
  89 + useImperativeHandle(ref, () => ({
  90 + validate: () => {
  91 + return form.validateFields();
  92 + },
  93 + }));
  94 +
  95 + const handleSave = (row: API.TradingGiftConfig) => {
  96 + onChange &&
  97 + onChange([
  98 + {
  99 + ...row,
  100 + },
  101 + ]);
  102 + };
  103 +
  104 + const components = {
  105 + body: {
  106 + row: EditableRow,
  107 + cell: EditableCell,
  108 + },
  109 + };
  110 +
  111 + const defaultColumns: (ColumnTypes[number] & { editable?: boolean; dataIndex: string })[] = [
  112 + {
  113 + title: '除以下场景外,都支持返现:',
  114 + dataIndex: 'conditions',
  115 + width: '40%',
  116 + render: (t: { name: string; value: string }[]) => (
  117 + <>{t.length ? t.map((item, index) => <div key={item.value}>{`${index + 1}.${item.name}`}</div>) : '--'}</>
  118 + ),
  119 + },
  120 + {
  121 + title: '返现比例',
  122 + dataIndex: 'vciReturnMoneyRate',
  123 + editable: !readOnly,
  124 + width: '30%',
  125 + render: (t) => (t ? `${t}%` : '--'),
  126 + },
  127 + {
  128 + title: '返现金券比例',
  129 + dataIndex: 'cashCouponRate',
  130 + editable: !readOnly,
  131 + width: '30%',
  132 + render: (t) => (t ? `${t}%` : '--'),
  133 + },
  134 + ];
  135 +
  136 + const columns = defaultColumns.map((col) => {
  137 + if (!col.editable) {
  138 + return col;
27 139 }
28   - }, [tradingGiftConfigList]);
29   -
30   - function edit(item: API.TradingGiftConfig) {
31   - setTradingGiftItem({ ...item });
32   - setTradingGiftVisible(true);
33   - }
34   -
35   - function _delete(item: API.TradingGiftConfig) {
36   - const { key } = item;
37   - const filterList = tradingGiftConfigList.filter((it) => it.key !== key);
38   - const _tradingGiftConfigList = filterList.map((it, index) => ({ ...it, key: index + 1 }));
39   - setTradingGiftConfigList([..._tradingGiftConfigList]);
40   - }
41   -
42   - /**
43   - * @description: 拼接优惠卷方式
44   - * @param {*} exchangeMethod
45   - * @return {*}
46   - */
47   - function giftExchangetoString(exchangeMethod: { label: string; value: string }[] = []) {
48   - const str = exchangeMethod.reduce((str, item, index) => {
49   - if (index + 1 !== exchangeMethod.length) {
50   - str += item.label + ' + ';
51   - } else {
52   - str += item.label;
53   - }
54   - return str;
55   - }, '');
56   - return str;
57   - }
  140 + return {
  141 + ...col,
  142 + onCell: (record: API.TradingGiftConfig) => ({
  143 + record,
  144 + editable: readOnly || col.editable,
  145 + dataIndex: col.dataIndex,
  146 + title: col.title,
  147 + handleSave,
  148 + }),
  149 + };
  150 + });
58 151  
59 152 return (
60   - <div>
61   - <div style={{ display: 'flex', justifyContent: 'flex-end', marginBottom: 10 }}>
62   - <Button
63   - type="link"
64   - icon={<PlusOutlined />}
65   - onClick={() => {
66   - setTradingGiftVisible(true);
67   - }}
68   - >
69   - 新增
70   - </Button>
71   - </div>
72   - <Table dataSource={tradingGiftConfigList || []} rowKey={(item, index) => `${index}-item`}>
73   - <Column
74   - title="前置条件"
75   - dataIndex="conditions"
76   - render={(t: { label: string; value: string }[], record: API.TradingGiftConfig) => (
77   - <>{t.length ? t.map((item, index) => <div key={item.value}>{`(${index + 1})${item.label}`}</div>) : <div>--</div>}</>
78   - )}
  153 + <Form form={form} component={false}>
  154 + <EditableContext.Provider value={form}>
  155 + <Table
  156 + // @ts-ignore
  157 + ref={ref}
  158 + components={components}
  159 + rowClassName={() => 'editable-row'}
  160 + bordered
  161 + dataSource={value}
  162 + pagination={false}
  163 + columns={columns as ColumnTypes}
79 164 />
80   - <Column
81   - title="礼品兑换方式"
82   - dataIndex="exchangeMethod"
83   - render={(t: { label: string; value: string }[], record: API.TradingGiftConfig) => <div>{giftExchangetoString(t)}</div>}
84   - />
85   - <Column
86   - title="操作"
87   - align="center"
88   - width="35%"
89   - render={(text, _item: API.TradingGiftConfig) => (
90   - <span>
91   - <a
92   - onClick={() => {
93   - edit(_item);
94   - }}
95   - style={{ marginLeft: 5 }}
96   - >
97   - {' '}
98   - {readOnly ? '查看' : '编辑'}
99   - </a>
100   - {!readOnly && (
101   - <>
102   - <Divider type="vertical" />
103   - <Popconfirm title="是否删除?" onConfirm={() => _delete(_item)} okText="确定" cancelText="取消">
104   - <a
105   - onClick={(e) => {
106   - e.preventDefault();
107   - }}
108   - style={{ color: 'red', marginLeft: 5 }}
109   - >
110   - 删除
111   - </a>
112   - </Popconfirm>
113   - </>
114   - )}
115   - </span>
116   - )}
117   - />
118   - </Table>
119   - <AddTradingGiftModal />
120   - </div>
  165 + </EditableContext.Provider>
  166 + </Form>
121 167 );
122 168 }
  169 +
  170 +export default forwardRef(TradingGiftConfig);
... ...
src/pages/carinsur/InsuranceTradingGift/entity.tsx renamed to src/pages/carinsur/InsuranceTradingGift/entity.ts
... ... @@ -4,24 +4,26 @@ import _ from &#39;lodash&#39;;
4 4 export const options = [
5 5 { label: '生效中', value: 1 },
6 6 { label: '未生效', value: 2 },
7   - { label: '已过期', value: 3 },
  7 + { label: '已失效', value: 3 },
8 8 ];
9 9  
10 10 export const typeList = [
11 11 { label: '新保', value: 1 },
12 12 { label: '续保', value: 3 },
  13 + { label: '零售', value: 4 },
13 14 ];
14 15  
15 16 export enum TypeEnum {
16 17 '--' = 0,
17 18 '新保' = 1,
18 19 '续保' = 3,
  20 + '零售' = 4,
19 21 }
20 22  
21 23 export enum StatusTextEnum {
22 24 '--' = 0,
23 25 '草稿' = 1,
24   - '审批中' = 2,
  26 + '审批中' = 2, // 审批状态废弃
25 27 '审批拒绝',
26 28 '审批超时',
27 29 '待生效',
... ... @@ -111,49 +113,30 @@ export function businessCarListFilter(list: API.ApplyCarList[]) {
111 113 export function filterGiftOption(list: API.TradingGiftConfig[]) {
112 114 const _list = _.cloneDeep(list);
113 115 return _list.map((item) => {
114   - item.conditions = item.conditions?.map((i) => i.value);
115   - item.exchangeMethod = item.exchangeMethod?.map((i) => i.value);
116   -
117 116 if (item.vciReturnMoneyRate) {
118 117 item.vciReturnMoneyRate = Number((item.vciReturnMoneyRate / 100).toFixed(4));
119 118 }
120   - if (item.daiReturnMoneyRate) {
121   - item.daiReturnMoneyRate = Number((item.daiReturnMoneyRate / 100).toFixed(4));
122   - }
123   - if (item.couponConfigList?.length) {
124   - item.couponConfigList = item.couponConfigList.map((it) => {
125   - delete it.key;
126   - delete it.disabled;
127   - return it;
128   - });
  119 + if (item.cashCouponRate) {
  120 + item.cashCouponRate = Number((item.cashCouponRate / 100).toFixed(4));
129 121 }
130   - delete item.key;
  122 + delete item.conditions;
131 123 return item;
132 124 });
133 125 }
134 126  
135   -/**
136   - * @description: 成交有礼条件配置过滤(后端-->业务)
137   - * @param {any} lsit
138   - * @return {*}
139   - */
140   -export function businessFilterGiftOption(list: any[]) {
  127 +export function combineGiftOption(list: API.TradingGiftConfig[]) {
141 128 const _list = _.cloneDeep(list);
142   - return _list.map((item, index) => {
143   - item.conditions = item.conditions?.map((i) => ({ value: i.value, label: i.name }));
144   - item.exchangeMethod = item.exchangeMethod?.map((i) => ({ value: i.value, label: i.name }));
  129 + return _list.map((item) => {
145 130 if (item.vciReturnMoneyRate) {
146   - item.daiReturnMoneyRate = (item.daiReturnMoneyRate * 100).toFixed(2);
147   - item.vciReturnMoneyRate = (item.vciReturnMoneyRate * 100).toFixed(2);
  131 + item.vciReturnMoneyRate = Number((item.vciReturnMoneyRate * 100).toFixed(4));
148 132 }
149   - if (item.couponConfigList?.length) {
150   - item.couponConfigList = item.couponConfigList.map((it: any, index: number) => {
151   - it.key = index + 1;
152   - it.disabled = it.validityPeriodType === 1;
153   - return it;
154   - });
  133 + if (item.cashCouponRate) {
  134 + item.cashCouponRate = Number((item.cashCouponRate * 100).toFixed(4));
155 135 }
156   - item.key = index + 1;
157   - return item;
  136 + return {
  137 + conditions: item.conditions,
  138 + vciReturnMoneyRate: item.vciReturnMoneyRate,
  139 + cashCouponRate: item.cashCouponRate,
  140 + };
158 141 });
159 142 }
... ...
src/pages/carinsur/InsuranceTradingGift/index.tsx
1   -import React, { useState } from 'react';
2   -import { Card, Radio, Row, Button } from 'antd';
  1 +import React from 'react';
  2 +import { Card, Row, Button } from 'antd';
3 3 import { PageHeaderWrapper } from '@ant-design/pro-layout';
4 4 import { PlusOutlined } from '@ant-design/icons';
5 5 import List from './components/List';
6 6 import Filter from './components/Filter';
7 7 import AddModal from './components/AddModal';
8   -import ShopModal from './components/ShopModal';
9 8 import CarModal from './components/CarModal';
10   -import ApprovalModal from './components/ApprovalModal';
11 9 import { createStore } from '@/hooks/moz';
12   -import store from './store';
13   -import { options } from './entity';
14   -import TerminateModal from './components/TerminateModal';
  10 +import store, { BtnTypeEnum } from './store';
15 11  
16 12 export const { Provider, useStore } = createStore(store);
17 13  
18 14 function TradingGift() {
19   - const { innerParams, setParams, setAddVisible, loading } = useStore();
20   - const [status, setStatus] = useState<number>(1);
21   -
22   - /**
23   - * @description: 状态切换
24   - * @param {number} value
25   - * @return {*}
26   - */
27   - function handleSwitchCut(value: number) {
28   - setStatus(value);
29   - setParams({ ...innerParams, statusGroup: value }, true);
30   - }
  15 + const { innerParams, setParams, setAddVisible, setBtnType } = useStore();
31 16  
32 17 function onSearch(params: any) {
33 18 setParams({ ...innerParams, ...params }, true);
... ... @@ -38,35 +23,22 @@ function TradingGift() {
38 23 * @return {*}
39 24 */
40 25 function handleAddTradingGift() {
  26 + setBtnType(BtnTypeEnum.新增);
41 27 setAddVisible(true);
42 28 }
43 29  
44 30 return (
45 31 <PageHeaderWrapper title="成交有礼配置">
46 32 <Card>
47   - <Row style={{ marginBottom: 20 }}>
48   - <Radio.Group
49   - optionType="button"
50   - options={options}
51   - value={status}
52   - disabled={loading}
53   - onChange={(value) => {
54   - handleSwitchCut(value.target?.value);
55   - }}
56   - />
57   - </Row>
58 33 <Row justify="space-between" style={{ marginBottom: 20 }}>
59 34 <Filter onChange={(params) => onSearch(params)} />
60 35 <Button icon={<PlusOutlined />} type="primary" onClick={handleAddTradingGift}>
61 36 新增
62 37 </Button>
63 38 </Row>
64   - <List status={status} />
  39 + <List />
65 40 <AddModal />
66   - <ShopModal />
67 41 <CarModal />
68   - <ApprovalModal />
69   - <TerminateModal />
70 42 </Card>
71 43 </PageHeaderWrapper>
72 44 );
... ...
src/pages/carinsur/InsuranceTradingGift/store.ts
... ... @@ -3,8 +3,12 @@ import usePagination from &#39;@/hooks/usePagination&#39;;
3 3 import useInitail from '@/hooks/useInitail';
4 4 import { getShopApi } from '@/common/api';
5 5 import * as API from './api';
6   -import moment from 'moment';
7   -import type { Moment } from 'moment';
  6 +
  7 +export enum BtnTypeEnum {
  8 + '新增' = 1,
  9 + '编辑',
  10 + '复制',
  11 +}
8 12  
9 13 function useStore() {
10 14 const {
... ... @@ -19,20 +23,14 @@ function useStore() {
19 23 const { data: shopsList } = useInitail(getShopApi, [], {});
20 24 const [addVisible, setAddVisible] = useState<boolean>(false);
21 25 const [confNo, setConfNo] = useState<string>('');
22   - const [type, setType] = useState<number | undefined>();
23 26 const [couponList, setCouponList] = useState<API.CouponResult[]>([]);
24   - const [tradingGiftConfigList, setTradingGiftConfigList] = useState<API.TradingGiftConfig[]>([]);
25 27 const [tradingGiftVisible, setTradingGiftVisible] = useState<boolean>(false);
26 28 const [couponConfigVisible, setCouponConfigVisible] = useState<boolean>(false);
27 29 const [tradingGiftItem, setTradingGiftItem] = useState<API.TradingGiftConfig>({});
28   - const [shopsModalData, setShopsModalData] = useState<{ shops?: API.Shops[]; visible?: boolean }>({ shops: [], visible: false });
29 30 const [carModalData, setCarModalData] = useState<{ cars?: API.ApplyCarList[]; visible?: boolean }>({ cars: [], visible: false });
30   - const [approvalProgressModalInfo, setApprovalProgressModalInfo] = useState<API.ApprovalProgress>({ visible: false });
31 31 const [buyInsuranceGiftConfigId, setBuyInsuranceGiftConfigId] = useState<number>();
32   - const [readOnly, setReadOnly] = useState<boolean>(false);
  32 + const [btnType, setBtnType] = useState<BtnTypeEnum>();
33 33  
34   - const [currItem, setCurrItem] = useState<API.PageItem>();
35   - const [terminateModalData, setTerminateModalData] = useState<{ date?: Moment; visible?: boolean }>({ date: moment(), visible: false });
36 34 return {
37 35 tradingGiftList,
38 36 paginationConfig,
... ... @@ -40,12 +38,8 @@ function useStore() {
40 38 innerParams,
41 39 setLoading,
42 40 loading,
43   - readOnly,
44   - setReadOnly,
45 41 couponList,
46 42 setCouponList,
47   - tradingGiftConfigList,
48   - setTradingGiftConfigList,
49 43 tradingGiftItem,
50 44 setTradingGiftItem,
51 45 addVisible,
... ... @@ -58,20 +52,12 @@ function useStore() {
58 52 shopsList,
59 53 confNo,
60 54 setConfNo,
61   - type,
62   - setType,
63   - shopsModalData,
64   - setShopsModalData,
65 55 carModalData,
66 56 setCarModalData,
67   - approvalProgressModalInfo,
68   - setApprovalProgressModalInfo,
69 57 buyInsuranceGiftConfigId,
70 58 setBuyInsuranceGiftConfigId,
71   - terminateModalData,
72   - setTerminateModalData,
73   - currItem,
74   - setCurrItem,
  59 + btnType,
  60 + setBtnType
75 61 };
76 62 }
77 63  
... ...
src/pages/carinsur/Rebate/subpages/RebateConfirm/Second/index.tsx
... ... @@ -116,7 +116,8 @@ function Index({ form }: Props) {
116 116 <div>
117 117 <Divider orientation="left" orientationMargin="0" />
118 118 <div style={{ fontWeight: 'bold', fontSize: 20, color: '#333', marginTop: 30, marginBottom: 20 }}>
119   - 应返利金额:<span style={{ color: '#FF921C' }}>{data?.totalRebateAmount ? rmb.p(data?.totalRebateAmount) : '--'}</span>元
  119 + 应返利金额:<span style={{ color: '#FF921C' }}>{typeof data?.totalRebateAmount === 'number' ? rmb.p(data?.totalRebateAmount) : '--'}</span>
  120 + 元
120 121 </div>
121 122 <Form form={form} layout="vertical">
122 123 <Form.Item
... ...
src/pages/carinsur/companyConfig/index.tsx
... ... @@ -60,7 +60,7 @@ export default function WorkStationIndex() {
60 60 </Button>
61 61 </Row>
62 62 <Table dataSource={list} pagination={paginationConfig} rowKey="id" loading={loading} className={indexS.table}>
63   - <Column title="保险公司" dataIndex="name" />
  63 + <Column title="保险公司" dataIndex="shortName" />
64 64 <Column
65 65 title="保单上保险公司名称"
66 66 dataIndex="orderInsurerNames"
... ... @@ -69,13 +69,11 @@ export default function WorkStationIndex() {
69 69 )}
70 70 />
71 71 <Column
72   - width="10%"
73 72 title="送修差额是否包含驾意险"
74 73 dataIndex="repairMarginContainsDai"
75 74 render={(value) => (typeof value === 'boolean' ? (value ? '是' : '否') : '-')}
76 75 />
77 76 <Column
78   - width="10%"
79 77 title="多送修是否累计到下月"
80 78 dataIndex="extraRepairToNextMonth"
81 79 render={(value) => (typeof value === 'boolean' ? (value ? '是' : '否') : '-')}
... ...
src/pages/carinsur/entity.ts
... ... @@ -6,6 +6,10 @@ export interface IdNameOption {
6 6 id: number;
7 7 name: string;
8 8 }
  9 +export interface ValueNameOption {
  10 + value: string;
  11 + name: string;
  12 +}
9 13 export enum LoanTypeEnum {
10 14 '购买即可' = 1,
11 15 '保费有要求',
... ...
src/pages/cas/afterSaleConfiguration/maintainConfig/api.ts
1 1 import { http } from '@/typing/http';
2 2 import request from '@/utils/request';
3   -import { CAS_HOST, OOP_HOST, PMS_HOST } from '@/utils/host';
  3 +import { CAS_HOST, OOP_HOST, PMS_HOST } from '@/utils/host'; // 列表
4 4  
5 5 // 列表
6 6 export function listApi(params: Maintain.ListParams): http.PromisePageResp<Maintain.ListVO> {
... ... @@ -57,8 +57,10 @@ export interface PartItem {
57 57 partNo?: string;
58 58 /**配件数量 */
59 59 partNumber?: number;
  60 +
60 61 [key: string]: any;
61 62 }
  63 +
62 64 export interface QueryParams {
63 65 current?: number;
64 66 pageSize?: number;
... ... @@ -70,3 +72,17 @@ export interface QueryParams {
70 72 export function getPartItems(params: QueryParams): PromisePageResp<PartItem> {
71 73 return request.get(`${PMS_HOST}/erp/part/list`, { params });
72 74 }
  75 +
  76 +export interface OilItem {
  77 + oilBrandId: number; // 机油品牌id
  78 + oilBrandName: string; // 机油品牌名称
  79 + oilModel: string; // 机油型号
  80 + oilViscosity: string; // 机油粘稠度
  81 + oilLevel: string; // 机油等级
  82 + motorOilType: string; // 机油类型
  83 +}
  84 +
  85 +/** 油料组合 */
  86 +export function oilGroupListApi(): http.PromiseResp<Maintain.OilGroup[]> {
  87 + return request.get(`${CAS_HOST}/erp/basic/maintenance/config/oil/combination`);
  88 +}
... ...
src/pages/cas/afterSaleConfiguration/maintainConfig/interface.d.ts
... ... @@ -118,6 +118,7 @@ declare namespace Maintain {
118 118 }
119 119  
120 120 interface OilGroup {
  121 + id: number;
121 122 oilBrandId: number; //机油品牌id
122 123 oilBrandName: string; //机油品牌名称
123 124 oilModel: string; //机油型号名称
... ...
src/pages/cas/afterSaleConfiguration/maintainConfig/subpages/MaintainEdit/components/EngineOilSelector/components/NewOilModal/index.tsx deleted
1   -import React, { useEffect, useState } from 'react';
2   -import { Form, Modal, Select } from 'antd';
3   -import useInitail from '@/hooks/useInitail';
4   -
5   -import { motorOilTypeData, oilViscosityData } from '@/pages/pms/entity';
6   -import { getBrandApi, getTypes } from '@/pages/cas/afterSaleConfiguration/maintainConfig/subpages/MaintainEdit/api';
7   -
8   -interface Props {
9   - selected: Maintain.OilGroup[];
10   - visible: boolean;
11   - onCancel: () => void;
12   - onOk: (data: Maintain.OilGroup[]) => any;
13   - current: Maintain.OilGroup;
14   - setCurrent: (value: any) => any;
15   -}
16   -
17   -function NewOilModal(props: Props) {
18   - const { visible, onCancel, onOk, selected, current, setCurrent } = props;
19   - const [form] = Form.useForm();
20   - const [delay, setDelay] = useState(true);
21   - const { data, setParams: setBrandParams } = useInitail<PmsPartOilSpace.Brand[], object>(getBrandApi, [], {}, delay);
22   - const { data: oilLevelData, setParams: setOilLevelParams } = useInitail<PmsPartOilSpace.Type[], string>(getTypes, [], 'oil_level', delay);
23   - const { data: oilModels, setParams } = useInitail<PmsPartOilSpace.Type[], string>(getTypes, [], 'oil_model', delay);
24   - const { data: motorOilTypeData, setParams: oilType } = useInitail < PmsPartOilSpace.Type[], string> (getTypes, [], 'oil_type', delay);
25   - const { data: oilViscosityData, setParams: oilViscosity } = useInitail < PmsPartOilSpace.Type[], string> (getTypes, [], 'oil_viscosity', delay);
26   - useEffect(() => {
27   - if (visible) {
28   - setDelay(false);
29   - setBrandParams({ oilType: 1 }, true);
30   - oilType('oil_type', true);
31   - oilViscosity('oil_viscosity', true);
32   - setOilLevelParams('oil_level', true);
33   - if (current.oilBrandId) {
34   - form.setFieldsValue({ ...current });
35   - setParams(`oil_model_${current.oilBrandId}`, true);
36   - } else {
37   - form.resetFields();
38   - }
39   - }
40   - }, [visible]);
41   -
42   - function _onOk(value: any) {
43   - const data: Maintain.OilGroup = {
44   - ...value,
45   - oilBrandName: value.oil.label,
46   - oilBrandId: value.oil.value,
47   - defaultUse: false,
48   - };
49   - const index = selected.findIndex((e) => {
50   - return (
51   - e.oilBrandId === value.oil.value &&
52   - e.motorOilType === value.motorOilType &&
53   - e.oilLevel === value.oilLevel &&
54   - e.oilModel === value.oilModel &&
55   - e.oilViscosity === value.oilViscosity
56   - );
57   - });
58   - if (current.index || current.index == 0) {
59   - selected[current.index] = data;
60   - } else if (index > -1) {
61   - selected[index] = data;
62   - } else {
63   - selected.push(data);
64   - }
65   -
66   - setCurrent({});
67   - onOk && onOk(selected);
68   - }
69   -
70   - const layout = {
71   - labelCol: { span: 4 },
72   - wrapperCol: { span: 20 },
73   - };
74   -
75   - return (
76   - <Modal width={650} forceRender title={`${current.oilBrandId ? '编辑' : '添加'}机油`} open={visible} onOk={form.submit} onCancel={onCancel}>
77   - <Form {...layout} labelAlign="left" form={form} onFinish={_onOk} component={false}>
78   - <Form.Item label="机油品牌" name="oil" rules={[{ required: true }]}>
79   - <Select
80   - labelInValue
81   - placeholder="请选择机油品牌"
82   - showSearch
83   - optionFilterProp="children"
84   - onChange={(id) => {
85   - setParams(`oil_model_${id.value}`, true);
86   - form.resetFields(['oilModel']);
87   - }}
88   - >
89   - {data.map((i) => (
90   - <Select.Option value={i.brandId || 0} key={i.brandId || 0}>
91   - {i.brandName || '--'}
92   - </Select.Option>
93   - ))}
94   - </Select>
95   - </Form.Item>
96   - <Form.Item label="机油型号" name="oilModel" rules={[{ required: true }]}>
97   - <Select placeholder="请选择机油型号" showSearch optionFilterProp="children">
98   - {oilModels.map((i) => (
99   - <Select.Option value={i.value || ''} key={i.value || ''}>
100   - {i.value || '--'}
101   - </Select.Option>
102   - ))}
103   - </Select>
104   - </Form.Item>
105   - <Form.Item label="机油类型" name="motorOilType" rules={[{ required: true }]}>
106   - <Select placeholder="请选择机油类型" showSearch optionFilterProp="children">
107   - {motorOilTypeData.map((i) => (
108   - <Select.Option value={i.value || 0} key={i.value || 0}>
109   - {i.value || '--'}
110   - </Select.Option>
111   - ))}
112   - </Select>
113   - </Form.Item>
114   - <Form.Item label="机油等级" name="oilLevel" rules={[{ required: true }]}>
115   - <Select placeholder="请选择机油等级" showSearch optionFilterProp="children">
116   - {oilLevelData.map((i) => (
117   - <Select.Option value={i.value || 0} key={i.value || 0}>
118   - {i.value || '--'}
119   - </Select.Option>
120   - ))}
121   - </Select>
122   - </Form.Item>
123   - <Form.Item label="粘稠度" name="oilViscosity" rules={[{ required: true }]}>
124   - <Select placeholder="请选择粘稠度" showSearch optionFilterProp="children">
125   - {oilViscosityData.map((i) => (
126   - <Select.Option value={i.value || 0} key={i.value || 0}>
127   - {i.value || '--'}
128   - </Select.Option>
129   - ))}
130   - </Select>
131   - </Form.Item>
132   - </Form>
133   - </Modal>
134   - );
135   -}
136   -
137   -export default NewOilModal;
src/pages/cas/afterSaleConfiguration/maintainConfig/subpages/MaintainEdit/components/EngineOilSelector/components/OilSelector.tsx 0 → 100644
  1 +import React, { useEffect, useState } from 'react';
  2 +import { Modal, Spin, Table } from 'antd';
  3 +
  4 +import useInitail from '@/hooks/useInitail';
  5 +
  6 +import { oilGroupListApi } from '@/pages/cas/afterSaleConfiguration/maintainConfig/api';
  7 +import st from './style.less';
  8 +
  9 +const Column = Table.Column;
  10 +
  11 +interface Props {
  12 + visible: boolean;
  13 + selected: Maintain.OilGroup[]; // 已选机油
  14 + onCancel: () => void;
  15 + onConfirm: (selectItems: Maintain.OilGroup[]) => void;
  16 +}
  17 +
  18 +/**
  19 + * 油料选择器
  20 + */
  21 +export default function OilSelector({ visible, selected, onCancel, onConfirm }: Props) {
  22 + const [delay, setDelay] = useState(true);
  23 + const [selectItems, setSelectItems] = useState<Maintain.OilGroup[]>([]);
  24 +
  25 + const { data, loading, setParams } = useInitail(oilGroupListApi, [], undefined, delay);
  26 +
  27 + useEffect(() => {
  28 + if (visible) {
  29 + setDelay(false);
  30 + setParams(undefined, true);
  31 +
  32 + if (selected?.length > 0) {
  33 + setSelectItems(selected);
  34 + }
  35 + }
  36 + }, [visible, selected]);
  37 +
  38 + const handleConfirm = () => {
  39 + onConfirm(selectItems);
  40 + setSelectItems([]);
  41 + };
  42 +
  43 + return (
  44 + <Modal
  45 + title="选择作机油"
  46 + width={780}
  47 + open={visible}
  48 + okButtonProps={{ disabled: selectItems.length === 0 }}
  49 + onOk={handleConfirm}
  50 + onCancel={onCancel}
  51 + destroyOnClose
  52 + >
  53 + <Spin spinning={loading}>
  54 + <Table
  55 + loading={loading}
  56 + dataSource={data}
  57 + rowClassName={st.clickableRow}
  58 + rowKey="id"
  59 + rowSelection={{
  60 + type: 'checkbox',
  61 + selectedRowKeys: selectItems.map((si) => si.id) || [],
  62 + onChange: (selectedRowKeys, selectedRows: Maintain.OilGroup[]) => {
  63 + setSelectItems(selectedRows);
  64 + },
  65 + }}
  66 + onRow={(record) => ({
  67 + onClick: () => {
  68 + if (selectItems.some((si) => si.id === record.id)) {
  69 + setSelectItems([...selectItems.filter((si) => si.id !== record.id)]);
  70 + } else {
  71 + setSelectItems([...selectItems, record]);
  72 + }
  73 + },
  74 + })}
  75 + footer={() => (
  76 + <span>
  77 + 已选:<span className={st.colorPrimary}>{`${selectItems.length} `}</span>项
  78 + </span>
  79 + )}
  80 + >
  81 + <Column title="机油品牌" dataIndex="oilBrandName" />
  82 + <Column title="型号" dataIndex="oilModel" />
  83 + <Column title="类型" dataIndex="motorOilType" />
  84 + <Column title="等级" dataIndex="oilLevel" />
  85 + <Column title="粘稠度" dataIndex="oilViscosity" />
  86 + </Table>
  87 + </Spin>
  88 + </Modal>
  89 + );
  90 +}
... ...
src/pages/cas/afterSaleConfiguration/maintainConfig/subpages/MaintainEdit/components/EngineOilSelector/components/style.less 0 → 100644
  1 +.clickableRow {
  2 + cursor: pointer;
  3 +}
0 4 \ No newline at end of file
... ...
src/pages/cas/afterSaleConfiguration/maintainConfig/subpages/MaintainEdit/components/EngineOilSelector/index.tsx
1 1 import React, { useEffect, useState } from 'react';
2   -import { Button, Row, Table } from 'antd';
  2 +import { Button, Row, Table, Tag } from 'antd';
3 3 import { MenuOutlined } from '@ant-design/icons';
4 4 import type { SortableContainerProps, SortEnd } from 'react-sortable-hoc';
5 5 import { SortableContainer, SortableElement, SortableHandle } from 'react-sortable-hoc';
6 6 import { arrayMoveImmutable } from 'array-move';
7 7  
8   -import NewOilModal from './components/NewOilModal';
  8 +import OilSelector from './components/OilSelector';
9 9 import OilData from './components/OilData';
10 10  
11 11 import './style.less';
... ... @@ -20,18 +20,36 @@ export interface Props {
20 20 oilGroups: Maintain.OilGroup[];
21 21 capacity: number;
22 22 disabled?: boolean;
23   - onChange?: (data: any[]) => void;
  23 + onChange?: (data: Maintain.OilGroup[]) => void;
24 24 }
25 25  
26   -export default function EngineOilSelector(props: Props) {
27   - const { oilGroups, capacity, disabled, onChange } = props;
28   -
  26 +export default function EngineOilSelector({ oilGroups, capacity, disabled, onChange }: Props) {
29 27 const [visible, setVisible] = useState<boolean>(false);
30   - const [current, setCurrent] = useState({} as Maintain.OilGroup);
31 28 const [oilData, setOilData] = useState<any>({ visible: false, data: {} });
32 29  
33 30 const [dataSource, setDataSource] = useState<any[]>([]);
34 31  
  32 + useEffect(() => {
  33 + const _list = oilGroups.map((e) => {
  34 + return {
  35 + ...e,
  36 + // 拼接 e 中属性为 id值
  37 + id: `${e.oilBrandName}-${e.oilModel}-${e.motorOilType}-${e.oilLevel}-${e.oilViscosity}`
  38 + };
  39 + });
  40 + setDataSource([..._list]);
  41 + }, [oilGroups]);
  42 +
  43 + const handleOilSelect = (selectOils: Maintain.OilGroup[]) => {
  44 + onChange && onChange(selectOils);
  45 + setVisible(false);
  46 + };
  47 +
  48 + const handleOilDelete = (index: number) => {
  49 + oilGroups.splice(index, 1);
  50 + onChange && onChange(oilGroups);
  51 + };
  52 +
35 53 const onSortEnd = ({ oldIndex, newIndex }: SortEnd) => {
36 54 if (oldIndex !== newIndex) {
37 55 const newData = arrayMoveImmutable(dataSource.slice(), oldIndex, newIndex).filter((el: any) => !!el);
... ... @@ -50,41 +68,6 @@ export default function EngineOilSelector(props: Props) {
50 68 return <SortableItem index={index} {...restProps} />;
51 69 };
52 70  
53   - useEffect(() => {
54   - const _list = oilGroups.map((e, i) => {
55   - return {
56   - ...e,
57   - id: i,
58   - };
59   - });
60   - setDataSource([..._list]);
61   - }, [oilGroups]);
62   -
63   - function _onOk(data: Maintain.OilGroup[]) {
64   - onChange && onChange(data);
65   - setVisible(false);
66   - }
67   -
68   - function onDelete(record: Maintain.OilGroup) {
69   - const _items = oilGroups;
70   - const index = _items.findIndex((e) => {
71   - return (
72   - e.oilBrandId === record.oilBrandId &&
73   - e.motorOilType === record.motorOilType &&
74   - e.oilLevel === record.oilLevel &&
75   - e.oilModel === record.oilModel &&
76   - e.oilViscosity === record.oilViscosity
77   - );
78   - });
79   - _items.splice(index, 1);
80   - _onOk(_items);
81   - }
82   -
83   - function edit(record: Maintain.OilGroup, index: number) {
84   - setCurrent({ ...record, oil: { value: record.oilBrandId, label: record.oilBrandName }, index });
85   - setVisible(true);
86   - }
87   -
88 71 return (
89 72 <div style={{ marginTop: 30 }}>
90 73 <Row justify="space-between" align="middle" style={{ marginBottom: 16 }}>
... ... @@ -112,7 +95,22 @@ export default function EngineOilSelector(props: Props) {
112 95 }}
113 96 >
114 97 <Column align="center" className="drag-visible" title="拖拽排序" dataIndex="sort" width={100} render={() => <DragHandle />} />
115   - <Column className="drag-visible" title="推荐顺序" dataIndex="oilIndex" width={100} render={(text, record, index) => <div>{index + 1}</div>} />
  98 + <Column
  99 + className="drag-visible"
  100 + title="推荐顺序"
  101 + dataIndex="oilIndex"
  102 + width={160}
  103 + render={(text, record, index) => (
  104 + <div>
  105 + {index + 1}
  106 + {index === 0 && (
  107 + <Tag style={{ marginLeft: 5 }} color={'#FF9211'}>
  108 + 厂家首保机油
  109 + </Tag>
  110 + )}
  111 + </div>
  112 + )}
  113 + />
116 114 <Column className="drag-visible" title="机油品牌" dataIndex="oilBrandName" />
117 115 <Column className="drag-visible" title="机油型号" dataIndex="oilModel" />
118 116 <Column className="drag-visible" title="机油类型" dataIndex="motorOilType" />
... ... @@ -134,32 +132,20 @@ export default function EngineOilSelector(props: Props) {
134 132 width={150}
135 133 align="center"
136 134 render={(text, record: Maintain.OilGroup, index) => (
137   - <>
138   - <Button onClick={() => edit(record, index)} type="link" size="small">
139   - 编辑
140   - </Button>
141   - <Button onClick={() => onDelete(record)} type="link" danger size="small">
142   - 删除
143   - </Button>
144   - </>
  135 + <Button onClick={() => handleOilDelete(index)} type="link" danger size="small">
  136 + 删除
  137 + </Button>
145 138 )}
146 139 />
147 140 </Table>
148   - <NewOilModal
149   - selected={[...oilGroups]}
150   - onOk={_onOk}
151   - visible={visible}
152   - onCancel={() => setVisible(false)}
153   - current={current}
154   - setCurrent={setCurrent}
155   - />
  141 +
  142 + <OilSelector visible={visible} selected={dataSource} onCancel={() => setVisible(false)} onConfirm={handleOilSelect} />
156 143 <OilData
157 144 visible={oilData.visible}
  145 + data={oilData.data}
158 146 onCancel={() => {
159 147 setOilData({ visible: false, data: {} });
160   - setCurrent({} as Maintain.OilGroup);
161 148 }}
162   - data={oilData.data}
163 149 />
164 150 </div>
165 151 );
... ...
src/pages/cas/afterSaleConfiguration/maintainConfig/subpages/MaintainEdit/components/MachineFilter/components/EngineFilterModal/index.tsx
... ... @@ -4,6 +4,7 @@ import debounce from &#39;lodash/debounce&#39;;
4 4 import Search from 'antd/lib/input/Search';
5 5  
6 6 import { getPartItems } from '../../../../../../api';
  7 +import st from '@/pages/cas/afterSaleConfiguration/maintainConfig/subpages/MaintainEdit/components/EngineOilSelector/components/style.less';
7 8  
8 9 const { Column } = Table;
9 10  
... ... @@ -86,6 +87,9 @@ function EngineOilModal(props: Props) {
86 87 <Table
87 88 dataSource={data}
88 89 loading={loading}
  90 + pagination={{ total, defaultPageSize: 10, current, pageSize, onChange: onPageChange, onShowSizeChange: onPageChange }}
  91 + rowClassName={st.clickableRow}
  92 + rowKey="partCode"
89 93 rowSelection={{
90 94 selectedRowKeys: selectedRow.map((item) => item.partCode),
91 95 onSelect: (row: any, _selected: boolean) => {
... ... @@ -111,8 +115,15 @@ function EngineOilModal(props: Props) {
111 115 setSelectedRow(newData);
112 116 },
113 117 }}
114   - pagination={{ total, defaultPageSize: 10, current, pageSize, onChange: onPageChange, onShowSizeChange: onPageChange }}
115   - rowKey="partCode"
  118 + onRow={(record) => ({
  119 + onClick: () => {
  120 + if (selectedRow.some((si) => si.partCode === record.partCode)) {
  121 + setSelectedRow([...selectedRow.filter((si) => si.partCode !== record.partCode)]);
  122 + } else {
  123 + setSelectedRow([...selectedRow, record]);
  124 + }
  125 + },
  126 + })}
116 127 >
117 128 <Column title="名称" dataIndex="partName" width={250} />
118 129 <Column title="编号" dataIndex="partCode" />
... ...
src/pages/cas/afterSaleConfiguration/maintainConfig/subpages/MaintainEdit/components/PartFilter/index.tsx
... ... @@ -105,7 +105,8 @@ export default function PartFilter({ partFilters, brandId, onChange }: Props) {
105 105 }
106 106  
107 107 const EditableCell: React.FC<EditableCellProps> = ({ editing, dataIndex, title, inputType, record, index, children, ...restProps }) => {
108   - const inputNode = <InputNumber />;
  108 + // todo 液态配件可以输入小数
  109 + const inputNode = <InputNumber precision={0} />;
109 110  
110 111 return (
111 112 <td {...restProps}>
... ... @@ -142,8 +143,8 @@ export default function PartFilter({ partFilters, brandId, onChange }: Props) {
142 143 <Table
143 144 dataSource={dataSource}
144 145 bordered
145   - rowClassName="editable-row"
146 146 pagination={false}
  147 + rowClassName="editable-row"
147 148 rowKey={(row) => `${row.partCode}_${row.partName}`}
148 149 components={{
149 150 body: {
... ...
src/pages/cas/afterSaleConfiguration/maintainConfig/subpages/MaintainEdit/index.tsx
1 1 import React, { useEffect, useState } from 'react';
2   -import { Button, Card, Empty, Form, Input, message, Row, Select, Spin, Tabs } from 'antd';
  2 +import { Button, Card, Empty, Form, Input, message, Modal, Row, Select, Spin, Tabs } from 'antd';
3 3 import { PageHeaderWrapper } from '@ant-design/pro-layout';
4 4 import useInitial from '@/hooks/useInitail';
5 5 import { ConnectProps } from '@/typing/common';
... ... @@ -411,8 +411,11 @@ export default function ConfigPage({ match }: Props) {
411 411 }
412 412 })
413 413 .catch((e) => {
414   - message.error(e.message);
415 414 setSubmitting(false);
  415 + Modal.error({
  416 + title: '提示',
  417 + content: e.message,
  418 + });
416 419 });
417 420 }
418 421  
... ...
src/pages/ehr/RoleManagementGroup/api.ts
... ... @@ -4,18 +4,16 @@
4 4 * @LastEditors: wangqiang@feewee.cn
5 5 * @LastEditTime: 2023-05-09 09:49:07
6 6 */
7   -import request from "@/utils/request";
8   -import { EHR_HOST } from "@/utils/host";
9   -import { http } from "@/typing/http";
  7 +import request from '@/utils/request';
  8 +import { EHR_HOST } from '@/utils/host';
  9 +import { http } from '@/typing/http';
10 10  
11 11 /**
12 12 * @description: 获取角色管理组列表
13 13 * @param {RoleManagementGrooup.QueryParams} params
14 14 * @return {http.PromisePageResp<RoleManagementGrooup.GroupItem>}
15 15 */
16   -export function getListApi(
17   - params: RoleManagementGrooup.QueryParams
18   -): http.PromisePageResp<RoleManagementGrooup.GroupItem> {
  16 +export function getListApi(params: RoleManagementGrooup.QueryParams): http.PromisePageResp<RoleManagementGrooup.GroupItem> {
19 17 return request.get(`${EHR_HOST}/roleStep/list`, { params });
20 18 }
21 19  
... ... @@ -24,9 +22,7 @@ export function getListApi(
24 22 * @param {RoleManagementGrooup.Detail} params
25 23 * @return {http.PromiseResp<string>}
26 24 */
27   -export function saveGroupApi(
28   - params: RoleManagementGrooup.Detail
29   -): http.PromiseResp<string> {
  25 +export function saveGroupApi(params: RoleManagementGrooup.Detail): http.PromiseResp<string> {
30 26 return request.post(`${EHR_HOST}/roleStep/save`, params);
31 27 }
32 28  
... ... @@ -35,9 +31,7 @@ export function saveGroupApi(
35 31 * @param {number} id
36 32 * @return {http.PromiseResp<RoleManagementGrooup.GroupRoleStepItem[]>}
37 33 */
38   -export function getGroupDetailApi(
39   - id: number
40   -): http.PromiseResp<RoleManagementGrooup.GroupRoleStepItem[]> {
  34 +export function getGroupDetailApi(id: number): http.PromiseResp<RoleManagementGrooup.GroupRoleStepItem[]> {
41 35 return request.get(`${EHR_HOST}/roleStep/step/info`, { params: { id } });
42 36 }
43 37  
... ... @@ -49,3 +43,12 @@ export function getGroupDetailApi(
49 43 export function deleteGroupApi(id: number): http.PromiseResp<string> {
50 44 return request.get(`${EHR_HOST}/roleStep/remove`, { params: { id } });
51 45 }
  46 +
  47 +/**
  48 + * @description: 获取角色组对应组织架构
  49 + * @param {number} stepId
  50 + * @return {http.PromiseResp<RoleManagementGrooup.RoleStaffTreeListVO>}
  51 + */
  52 +export function getRoleStaffTreeApi(stepId: number): http.PromiseResp<RoleManagementGrooup.RoleStaffTreeListVO> {
  53 + return request.get(`${EHR_HOST}/hr/common/role/step/manage/tree`, { params: { stepId } });
  54 +}
... ...
src/pages/ehr/RoleManagementGroup/components/List.tsx
... ... @@ -2,28 +2,28 @@
2 2 * @Author: wangqiang@feewee.cn
3 3 * @Date: 2022-06-29 10:35:41
4 4 * @LastEditors: wangqiang@feewee.cn
5   - * @LastEditTime: 2022-07-01 15:20:41
  5 + * @LastEditTime: 2024-04-19 17:15:41
6 6 */
7   -import { Divider, message, Popconfirm, Table } from "antd";
8   -import moment from "moment";
9   -import React from "react";
10   -import { useStore } from "../index";
11   -import { deleteGroupApi } from "../api";
  7 +import { Divider, message, Popconfirm, Table } from 'antd';
  8 +import moment from 'moment';
  9 +import React from 'react';
  10 +import { useStore } from '../index';
  11 +import { deleteGroupApi } from '../api';
12 12 // import SubmitModal from "@/pages/ehr/Insurance/components/SubmitModal";
13 13  
14 14 export default function RoleManagementGroupList() {
15   - const { pagination, getDetail, current, setCurrent } = useStore();
  15 + const { elements, pagination, getDetail, setType, setCurrent } = useStore();
16 16 // const [deleteVisible, setDeleteVisible] = useState(false);
17 17  
18 18 const deleteGroup = (id: number) => {
19   - const hide = message.loading("删除中...", 0);
  19 + const hide = message.loading('删除中...', 0);
20 20 deleteGroupApi(id)
21 21 .then((res) => {
22 22 message.success(res.result);
23 23 pagination.setLoading(true);
24 24 })
25 25 .catch((err) => {
26   - message.error(err.message || "删除失败");
  26 + message.error(err.message || '删除失败');
27 27 })
28 28 .finally(() => {
29 29 setCurrent(undefined);
... ... @@ -32,59 +32,40 @@ export default function RoleManagementGroupList() {
32 32 });
33 33 };
34 34  
  35 + function getRoleStaffTree(item: RoleManagementGrooup.GroupItem) {
  36 + setCurrent(item);
  37 + setType('tree');
  38 + }
  39 +
35 40 return (
36   - <>
37   - <Table
38   - dataSource={pagination.list}
39   - rowKey="id"
40   - loading={pagination.loading}
41   - pagination={pagination.paginationConfig}
42   - >
43   - <Table.Column title="角色管理组名" align="left" dataIndex="name" />
44   - <Table.Column
45   - title="创建时间"
46   - align="left"
47   - render={(record: RoleManagementGrooup.GroupItem) => (record.createTime
48   - ? moment(record.createTime).format("YYYY-MM-DD HH:mm")
49   - : "-")}
50   - />
51   - <Table.Column
52   - title="操作"
53   - align="left"
54   - render={(record: RoleManagementGrooup.GroupItem) => (
55   - <>
56   - <a onClick={() => getDetail(record, false)}>查看</a>
57   - <Divider type="vertical" />
58   - <a onClick={() => getDetail(record, true)}>编辑</a>
59   - <Divider type="vertical" />
60   - {/* <a
61   - style={{ color: "red" }}
62   - onClick={() => {
63   - setCurrent(record);
64   - setDeleteVisible(true);
65   - }}
66   - >
67   - 删除
68   - </a> */}
69   - <Popconfirm
70   - title={`确定删除【${record.name}】角色管理组?`}
71   - onConfirm={() => deleteGroup(record.id || -1)}
72   - >
73   - <a style={{ color: "red" }}>删除</a>
74   - </Popconfirm>
75   - </>
76   - )}
77   - />
78   - </Table>
79   - {/* <SubmitModal
80   - closable={false}
81   - limit
82   - style={{ minWidth: 400 }}
83   - title={`确定删除【${current?.name}】角色管理组?`}
84   - visible={deleteVisible}
85   - onCancel={() => setDeleteVisible(false)}
86   - onOk={deleteGroup}
87   - /> */}
88   - </>
  41 + <Table dataSource={pagination.list} rowKey="id" loading={pagination.loading} pagination={pagination.paginationConfig}>
  42 + <Table.Column title="角色管理组名" align="left" dataIndex="name" />
  43 + <Table.Column
  44 + title="创建时间"
  45 + align="left"
  46 + render={(record: RoleManagementGrooup.GroupItem) => (record.createTime ? moment(record.createTime).format('YYYY-MM-DD HH:mm') : '-')}
  47 + />
  48 + <Table.Column
  49 + title="操作"
  50 + align="left"
  51 + render={(record: RoleManagementGrooup.GroupItem) => (
  52 + <>
  53 + {elements.includes('role:manage:edit') ? (
  54 + <>
  55 + <a onClick={() => getDetail(record, false)}>查看</a>
  56 + <Divider type="vertical" />
  57 + <a onClick={() => getDetail(record, true)}>编辑</a>
  58 + <Divider type="vertical" />
  59 + <Popconfirm title={`确定删除【${record.name}】角色管理组?`} onConfirm={() => deleteGroup(record.id || -1)}>
  60 + <a style={{ color: 'red' }}>删除</a>
  61 + </Popconfirm>
  62 + <Divider type="vertical" />
  63 + </>
  64 + ) : null}
  65 + <a onClick={() => getRoleStaffTree(record)}>查看组织架构</a>
  66 + </>
  67 + )}
  68 + />
  69 + </Table>
89 70 );
90 71 }
... ...
src/pages/ehr/RoleManagementGroup/components/ListIndex.tsx 0 → 100644
  1 +import React from 'react';
  2 +import List from './List';
  3 +import Modal from './Modal';
  4 +import FeeweeFilterOption from '@/pages/notice/components/FeeweeFilterOption';
  5 +import { useStore } from '../index';
  6 +import { Button, Input, Row } from 'antd';
  7 +import { PlusOutlined } from '@ant-design/icons';
  8 +
  9 +export default function ListIndex() {
  10 + const { elements, pagination, setVisible } = useStore();
  11 +
  12 + return (
  13 + <>
  14 + <Row justify="space-between">
  15 + <FeeweeFilterOption title="角色管理组名">
  16 + <Input.Search
  17 + style={{ minWidth: 260, marginBottom: 10, marginRight: 10 }}
  18 + allowClear
  19 + placeholder="请输入角色管理组名查询"
  20 + value={pagination.innerParams.name}
  21 + onChange={(e) => pagination.setParams({ name: e.target.value })}
  22 + onSearch={() => pagination.setParams({ current: 1 }, true)}
  23 + />
  24 + </FeeweeFilterOption>
  25 +
  26 + {elements.includes('role:manage:edit') ? (
  27 + <Button
  28 + // @ts-ignore
  29 + icon={<PlusOutlined />}
  30 + type="primary"
  31 + onClick={() => {
  32 + setVisible(true);
  33 + }}
  34 + >
  35 + 新增
  36 + </Button>
  37 + ) : null}
  38 + </Row>
  39 + <List />
  40 + <Modal />
  41 + </>
  42 + );
  43 +}
... ...
src/pages/ehr/RoleManagementGroup/components/RoleStaffTree.tsx 0 → 100644
  1 +import React, { useEffect, useRef, useState } from 'react';
  2 +import { useStore } from '../index';
  3 +import type { Point } from 'react-d3-tree';
  4 +import Tree from 'react-d3-tree';
  5 +import { getRoleStaffTreeApi } from '../api';
  6 +import { Empty, Spin, Modal, Button, Row, Divider } from 'antd';
  7 +import useInitial from '@/hooks/useInitail';
  8 +import DetailItem from '@/pages/ehr/Authentication/Settings/components/DetailItem';
  9 +
  10 +export default function RoleStaffTree() {
  11 + const { current } = useStore();
  12 + const treeInitital = useInitial(getRoleStaffTreeApi, {}, current?.id ?? -1);
  13 + const treeContainer = useRef<HTMLDivElement | null>(null);
  14 + const [translate, setTranslate] = useState<Point>({ x: 200, y: 20 });
  15 + const [dimensions, setDataimensions] = useState<{ width: number; height: number }>();
  16 +
  17 + const [modalInfo, setModalInfo] = useState<{ open: boolean; data?: RoleManagementGrooup.RoleStaffTreeVO }>({ open: false });
  18 +
  19 + useEffect(() => {
  20 + if (treeContainer.current) {
  21 + const rect = treeContainer.current.getBoundingClientRect();
  22 + setTranslate({ x: rect.width / 2, y: 20 });
  23 + setDataimensions({ width: rect.width, height: 20 });
  24 + }
  25 + }, [treeContainer.current]);
  26 +
  27 + return (
  28 + <div id="treeWrapper" style={{ height: 'calc(100vh - 288px)' }} ref={treeContainer}>
  29 + {treeInitital.loading ? (
  30 + <div style={{ width: '100%' }}>
  31 + <Spin spinning style={{ width: '100%', margin: '40px auto' }} />
  32 + </div>
  33 + ) : treeInitital.errMsg ? (
  34 + <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description={treeInitital.errMsg} />
  35 + ) : treeInitital.data.treeList?.length ? (
  36 + <Tree
  37 + data={treeInitital.data.treeList}
  38 + orientation="vertical"
  39 + separation={{ nonSiblings: 2, siblings: 2 }}
  40 + pathFunc="step"
  41 + onLinkClick={(...props) => console.log('onLinkClick', props)}
  42 + onNodeClick={(...props) => {
  43 + console.log('onNodeClick', props);
  44 + }}
  45 + scaleExtent={{ min: 0, max: 20 }}
  46 + enableLegacyTransitions
  47 + translate={translate}
  48 + initialDepth={treeInitital.data.levelCnt ?? 2}
  49 + dimensions={dimensions}
  50 + centeringTransitionDuration={500}
  51 + renderCustomNodeElement={({ nodeDatum, toggleNode }) => {
  52 + const data: RoleManagementGrooup.RoleStaffTreeVO = nodeDatum;
  53 + const level = data.level ?? '级别';
  54 + const width = 15 * level.length;
  55 + const x = -(width / 2);
  56 + const textX = width / 2 + 10;
  57 + return (
  58 + <g>
  59 + <rect
  60 + id="tree-item-toggle"
  61 + x={x}
  62 + width={width}
  63 + height="30"
  64 + fill={data.children?.length ? '#4189FD' : 'white'}
  65 + stroke={data.children?.length ? '#4189FD' : 'black'}
  66 + strokeWidth="0.5"
  67 + onClick={toggleNode}
  68 + />
  69 + <text
  70 + id="tree-item-toggle"
  71 + x="0"
  72 + y="15"
  73 + fontSize="12"
  74 + textAnchor="middle"
  75 + alignmentBaseline="middle"
  76 + strokeWidth="0.5"
  77 + fill={data.children?.length ? 'white' : 'black'}
  78 + stroke={data.children?.length ? 'white' : 'black'}
  79 + onClick={toggleNode}
  80 + >
  81 + {level}
  82 + </text>
  83 + <text fill="black" strokeWidth="1" fontSize={15} x={textX} y={15}>
  84 + {nodeDatum.name}&nbsp;&nbsp;
  85 + <tspan
  86 + textDecoration="underline"
  87 + fontSize={12}
  88 + strokeWidth={0.5}
  89 + fill="#FF921C"
  90 + stroke="#FF921C"
  91 + onClick={() => setModalInfo({ open: true, data })}
  92 + >
  93 + 查看详情
  94 + </tspan>
  95 + </text>
  96 + {Object.entries(nodeDatum.attributes ?? {}).map(([name, value], idx) => (
  97 + <text key={name} fill="#666" stroke="#666" strokeWidth="0.5" fontSize={12} x={textX} y={15 * (idx + 2)}>
  98 + {name}:{value}
  99 + </text>
  100 + ))}
  101 + </g>
  102 + );
  103 + }}
  104 + />
  105 + ) : null}
  106 + <Modal
  107 + title="人员详情"
  108 + open={modalInfo.open}
  109 + footer={<Button onClick={() => setModalInfo({ open: false })}>关闭</Button>}
  110 + onCancel={() => setModalInfo({ open: false })}
  111 + >
  112 + <Row style={{ gap: '0 30px' }}>
  113 + <DetailItem title="级别" desp={modalInfo.data?.level ?? '-'} />
  114 + <DetailItem title="员工" desp={modalInfo.data?.name ?? '-'} />
  115 + {Object.entries(modalInfo.data?.attributes ?? {}).map(([name, value]) => (
  116 + <DetailItem key={name} title={name} desp={value ?? '-'} />
  117 + ))}
  118 + </Row>
  119 + <Divider orientation="left">角色授权门店({modalInfo.data?.roleShopList?.length ?? '-'} 个门店 )</Divider>
  120 + <Row style={{ flexDirection: 'column', flexWrap: 'nowrap', alignItems: 'start', maxHeight: '50vh', overflowY: 'auto' }}>
  121 + {modalInfo.data?.roleShopList?.map((shop, idx) => (
  122 + <p key={shop} style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
  123 + <div style={{ width: 6, height: 6, borderRadius: '50%', backgroundColor: '#4189FD' }} />
  124 + <span style={{ flex: 1 }}>
  125 + {idx + 1}.&nbsp;{shop}
  126 + </span>
  127 + </p>
  128 + ))}
  129 + </Row>
  130 + </Modal>
  131 + </div>
  132 + );
  133 +}
... ...
src/pages/ehr/RoleManagementGroup/index.tsx
... ... @@ -2,51 +2,54 @@
2 2 * @Author: wangqiang@feewee.cn
3 3 * @Date: 2022-06-29 10:09:59
4 4 * @LastEditors: wangqiang@feewee.cn
5   - * @LastEditTime: 2023-09-02 16:12:33
  5 + * @LastEditTime: 2024-04-19 17:07:55
6 6 */
7   -import React from 'react';
  7 +import React, { useEffect, useMemo } from 'react';
8 8 import { createStore } from '@/hooks/moz';
9 9 import zhCN from 'antd/lib/locale-provider/zh_CN';
10 10 import { PageHeaderWrapper } from '@ant-design/pro-layout';
11   -import { Button, Card, ConfigProvider, Input, Row } from 'antd';
12   -import { PlusOutlined } from '@ant-design/icons';
  11 +import { Button, Card, ConfigProvider } from 'antd';
  12 +import { LeftCircleOutlined } from '@ant-design/icons';
13 13 import store from './store';
14   -import List from './components/List';
15   -import Modal from './components/Modal';
16   -import FeeweeFilterOption from '@/pages/notice/components/FeeweeFilterOption';
  14 +import List from './components/ListIndex';
  15 +import Tree from './components/RoleStaffTree';
17 16  
18 17 export const { Provider, useStore } = createStore(store);
19 18  
20 19 function RoleManagementGroup() {
21   - const { pagination, setVisible } = useStore();
  20 + const { type, current, setType, setCurrent } = useStore();
  21 +
  22 + const DOM = useMemo(() => {
  23 + if (type === 'list') {
  24 + return List;
  25 + } else {
  26 + return Tree;
  27 + }
  28 + }, [type]);
22 29  
23 30 return (
24   - <PageHeaderWrapper title="角色管理组">
  31 + <PageHeaderWrapper
  32 + title="角色管理组"
  33 + content={type === 'tree' ? current?.name ?? '-' : null}
  34 + extraContent={
  35 + type === 'tree' ? (
  36 + <Button
  37 + // @ts-ignore
  38 + icon={<LeftCircleOutlined />}
  39 + type="link"
  40 + onClick={() => {
  41 + setCurrent(undefined);
  42 + setType('list');
  43 + }}
  44 + >
  45 + 返回
  46 + </Button>
  47 + ) : null
  48 + }
  49 + >
25 50 <ConfigProvider locale={zhCN}>
26 51 <Card bordered={false}>
27   - <Row justify="space-between">
28   - <FeeweeFilterOption title="角色管理组名">
29   - <Input.Search
30   - style={{ minWidth: 260, marginBottom: 10, marginRight: 10 }}
31   - allowClear
32   - placeholder="请输入角色管理组名查询"
33   - value={pagination.innerParams.name}
34   - onChange={(e) => pagination.setParams({ name: e.target.value })}
35   - onSearch={() => pagination.setParams({ current: 1 }, true)}
36   - />
37   - </FeeweeFilterOption>
38   - <Button
39   - icon={<PlusOutlined />}
40   - type="primary"
41   - onClick={() => {
42   - setVisible(true);
43   - }}
44   - >
45   - 新增
46   - </Button>
47   - </Row>
48   - <List />
49   - <Modal />
  52 + <DOM />
50 53 </Card>
51 54 </ConfigProvider>
52 55 </PageHeaderWrapper>
... ...
src/pages/ehr/RoleManagementGroup/interface.d.ts
... ... @@ -27,4 +27,18 @@ declare namespace RoleManagementGrooup {
27 27 interface Role extends CommonApi.RoleCodeVO {
28 28 index?: number; // 所在级别,其他级别不能编辑
29 29 }
  30 +
  31 + interface RoleStaffTreeListVO {
  32 + treeList?: RoleStaffTreeVO[];
  33 + levelCnt?: number; //
  34 + }
  35 +
  36 + interface RoleStaffTreeVO {
  37 + staffId?: number; //
  38 + name: string; // 员工名
  39 + level?: string; // 级别
  40 + attributes?: Record<string, string | number | boolean>;
  41 + roleShopList?: string[]; // 授权门店
  42 + children?: RoleStaffTreeVO[];
  43 + }
30 44 }
... ...
src/pages/ehr/RoleManagementGroup/store.ts
... ... @@ -4,25 +4,25 @@
4 4 * @LastEditors: wangqiang@feewee.cn
5 5 * @LastEditTime: 2022-07-04 10:32:00
6 6 */
7   -import usePagination from "@/hooks/usePagination";
8   -import useInitial from "@/hooks/useInitail";
9   -import { getAllRoleCodeApi } from "@/common/api";
10   -import { useState } from "react";
11   -import { getListApi, getGroupDetailApi } from "./api";
12   -import { message } from "antd";
  7 +import usePagination from '@/hooks/usePagination';
  8 +import useInitial from '@/hooks/useInitail';
  9 +import { getAllRoleCodeApi } from '@/common/api';
  10 +import { useState } from 'react';
  11 +import { getListApi, getGroupDetailApi } from './api';
  12 +import { message } from 'antd';
  13 +import useMenuElement from '@/hooks/useMenuElement';
13 14  
14 15 export default function useStore() {
  16 + const { elements } = useMenuElement();
15 17 const pagination = usePagination(getListApi);
16   - const roleInitial = useInitial<
17   - RoleManagementGrooup.Role[],
18   - CommonApi.RoleParams
19   - >(getAllRoleCodeApi, [], { roleType: 2 }); // 默认查询所有管理角色
  18 + const roleInitial = useInitial<RoleManagementGrooup.Role[], CommonApi.RoleParams>(getAllRoleCodeApi, [], { roleType: 2 }); // 默认查询所有管理角色
20 19 const [visible, setVisible] = useState(false);
21 20 const [disabled, setDisabled] = useState(false);
22 21 const [current, setCurrent] = useState<RoleManagementGrooup.Detail>();
  22 + const [type, setType] = useState<'list' | 'tree'>('list');
23 23  
24 24 function getDetail(item: RoleManagementGrooup.GroupItem, edit: boolean) {
25   - const hide = message.loading("查询中...", 0);
  25 + const hide = message.loading('查询中...', 0);
26 26 getGroupDetailApi(item.id || -1)
27 27 .then((res) => {
28 28 setCurrent({
... ... @@ -33,7 +33,7 @@ export default function useStore() {
33 33 setVisible(true);
34 34 })
35 35 .catch((err) => {
36   - message.error(err.message || "查询失败");
  36 + message.error(err.message || '查询失败');
37 37 })
38 38 .finally(() => {
39 39 hide();
... ... @@ -45,6 +45,7 @@ export default function useStore() {
45 45 }
46 46  
47 47 return {
  48 + elements,
48 49 pagination,
49 50 roleInitial,
50 51 visible,
... ... @@ -53,21 +54,20 @@ export default function useStore() {
53 54 setDisabled,
54 55 current,
55 56 setCurrent,
  57 + type,
  58 + setType,
56 59 getDetail,
57 60 formatRoleList,
58 61 };
59 62 }
60 63  
61   -function handleRoleList(
62   - roleList: RoleManagementGrooup.Role[],
63   - codes?: (string | string[])[]
64   -): RoleManagementGrooup.Role[] {
  64 +function handleRoleList(roleList: RoleManagementGrooup.Role[], codes?: (string | string[])[]): RoleManagementGrooup.Role[] {
65 65 if (codes) {
66 66 roleList = roleList.map((role) => {
67   - let index = codes.findIndex((code) => {
  67 + const index = codes.findIndex((code) => {
68 68 if (Array.isArray(code) && code.includes(role.roleCode)) {
69 69 return true;
70   - } else if (typeof code === "string" && code === role.roleCode) {
  70 + } else if (typeof code === 'string' && code === role.roleCode) {
71 71 return true;
72 72 } else {
73 73 return false;
... ...
src/pages/oop/Dealer/Shop/config.tsx
... ... @@ -6,6 +6,7 @@ const SHOP_TYPE: Record&lt;number, string&gt; = {
6 6 5: '交付中心',
7 7 6: '配件库房',
8 8 7: '新车库房',
  9 + 8: '二网库房',
9 10 };
10 11  
11 12 export const bizTypeList = [
... ...
src/pages/order3/WeeklydataUpload/index.tsx
  1 +/*
  2 + * @Author: jiangwei jiangwei.feewee.cn
  3 + * @Date: 2024-04-19 10:49:37
  4 + * @LastEditors: jiangwei jiangwei.feewee.cn
  5 + * @LastEditTime: 2024-04-19 11:03:43
  6 + * @FilePath: /fw-cms/src/pages/order3/WeeklydataUpload/index.tsx
  7 + * @Description:
  8 + *
  9 + * Copyright (c) 2024 by ${git_name_email}, All Rights Reserved.
  10 + */
1 11 import React, { useState } from 'react'
2 12 import { PageHeaderWrapper } from '@ant-design/pro-layout'
3 13 import { Button, Card, Table, Tag, Divider, Select } from 'antd'
... ... @@ -80,7 +90,7 @@ export default function Index() {
80 90 <Column title="状态" dataIndex="status" render={t => <Tag color={t == 1 ? 'orange' : 'green'}>{t == 1 ? '处理中' : '已完成'}</Tag>} />
81 91 </Table>
82 92 </Card>
83   - <UploadModal visible={visible} onCancel={() => setVisible(false)} onRefshing={setParams} />
  93 + <UploadModal visible={visible} onCancel={() => setVisible(false)} onRefshing={() => setParams({}, true)} />
84 94 </PageHeaderWrapper>
85 95 )
86 96 }
87 97 \ No newline at end of file
... ...
src/pages/performance/KpiSetting/components/EditModal.tsx
... ... @@ -44,9 +44,10 @@ export default function EditModal({ onClose, setItem, item, roleList }: Props) {
44 44 useEffect(() => {
45 45 if (visible && currentItem) {
46 46 const result = transformFormData(currentItem, roleList, list);
47   - console.log(currentItem,'323')
48   - console.log({...result},'1111dwewwwwwew')
49   - form.setFieldsValue({ ...result});
  47 + console.log(currentItem, '323');
  48 + console.log({ ...result }, '1111dwewwwwwew');
  49 +
  50 + form.setFieldsValue({ ...result });
50 51 }
51 52 }, [visible]);
52 53 function handleSave(values: any) {
... ... @@ -203,6 +204,7 @@ export default function EditModal({ onClose, setItem, item, roleList }: Props) {
203 204 </Select>
204 205 </Form.Item>
205 206 )}
  207 +
206 208 <Form.Item
207 209 noStyle
208 210 shouldUpdate={(prevValues, currentValues) => {
... ... @@ -211,7 +213,7 @@ export default function EditModal({ onClose, setItem, item, roleList }: Props) {
211 213 >
212 214 {({ getFieldValue }) => {
213 215 return getFieldValue('roleType') === 3 ? (
214   - <Form.Item name="roles" label="适用角色1" rules={[{ required: true }]}>
  216 + <Form.Item name="roles" label="适用角色" rules={[{ required: true }]}>
215 217 <Select
216 218 placeholder="请选择角色"
217 219 showSearch
... ... @@ -231,18 +233,34 @@ export default function EditModal({ onClose, setItem, item, roleList }: Props) {
231 233 ) : null;
232 234 }}
233 235 </Form.Item>
234   - <Form.Item name="businessOriginName" label="原始指标名称" rules={[{ required: true, message: '请输入原始指标名称' }]}>
235   - <Input placeholder="请输入指标名称" />
236   - </Form.Item>
237   - <Form.Item name="businessOriginUnit" label="原始指标单位" rules={[{ required: true }]}>
238   - <Select disabled={isOriginIndicatorCode} placeholder="请选择原始指标单位">
239   - {UnitType.map((item) => (
240   - <Option value={item.value} key={item.value}>
241   - {item.label}
242   - </Option>
243   - ))}
244   - </Select>
  236 +
  237 + <Form.Item
  238 + noStyle
  239 + shouldUpdate={(prevValues, currentValues) => {
  240 + return prevValues.hasTarget !== currentValues.hasTarget || prevValues.originIndicatorCode !== currentValues.originIndicatorCode;
  241 + }}
  242 + >
  243 + {({ getFieldValue }) => {
  244 + const ok = getFieldValue('hasTarget') === false && !getFieldValue('originIndicatorCode');
  245 + return ok ? (
  246 + <>
  247 + <Form.Item name="businessOriginName" label="原始指标名称" rules={[{ message: '请输入原始指标名称' }]}>
  248 + <Input placeholder="请输入指标名称" />
  249 + </Form.Item>
  250 + <Form.Item name="businessOriginUnit" label="原始指标单位">
  251 + <Select disabled={isOriginIndicatorCode} placeholder="请选择原始指标单位">
  252 + {UnitType.map((item) => (
  253 + <Option value={item.value} key={item.value}>
  254 + {item.label}
  255 + </Option>
  256 + ))}
  257 + </Select>
  258 + </Form.Item>
  259 + </>
  260 + ) : null;
  261 + }}
245 262 </Form.Item>
  263 +
246 264 </Form>
247 265 </Modal>
248 266 );
... ...
src/pages/pms/partPlan/AppointPart/comonents/PartStorageModal.tsx
... ... @@ -55,8 +55,8 @@ export default function PartStorageModal({ onCancel, visible, onOk, itemData, se
55 55  
56 56 return (
57 57 <Modal
58   - width={800}
59   - visible={visible}
  58 + width={1000}
  59 + open={visible}
60 60 onCancel={() => {
61 61 onCancel();
62 62 setPartParams({});
... ... @@ -78,10 +78,10 @@ export default function PartStorageModal({ onCancel, visible, onOk, itemData, se
78 78 </Button>,
79 79 ]}
80 80 >
81   - <Form form={form} labelCol={{ span: 3 }} wrapperCol={{ span: 19 }}>
  81 + <Form form={form} labelCol={{ span: 2 }} wrapperCol={{ span: 19 }}>
82 82 <Item label="库房" name="storageId" required rules={[{ required: true, message: '请选择库房' }]}>
83 83 <PmsSelect
84   - style={{ width: 250 }}
  84 + style={{ width: 400 }}
85 85 placeholder="请选择库房"
86 86 value={storageid}
87 87 onChange={(v) => setStorageid(v)}
... ... @@ -90,7 +90,7 @@ export default function PartStorageModal({ onCancel, visible, onOk, itemData, se
90 90 </Item>
91 91 <Item label="供应商" name="supplierId" required rules={[{ required: true, message: '请选择供应商' }]}>
92 92 <PmsSelect
93   - style={{ width: 250 }}
  93 + style={{ width: 400 }}
94 94 placeholder="请选择供应商"
95 95 onChange={(v) => setPartParams({ stId: storageid, spId: v })}
96 96 options={suppliers
... ...
src/pages/pms/storage/LocationManage/index.tsx
... ... @@ -36,6 +36,21 @@ export default function RepertoryIndex() {
36 36 allowClear
37 37 options={shops.map(i => ({ value: i.shopId, label: i.shopShortName }))}
38 38 />
  39 + <Select
  40 + style={{ width: 200, marginRight: 10, marginBottom: 10 }}
  41 + placeholder="是否有库存筛选"
  42 + onChange={v => {
  43 + if (typeof v != 'undefined') {
  44 + setParams({ hasStock: !!v }, true)
  45 + } else {
  46 + setParams({ hasStock: undefined }, true)
  47 + }
  48 + }}
  49 + showSearch
  50 + optionFilterProp="children"
  51 + allowClear
  52 + options={[{ value: 1, label: '是' }, { value: 0, label: '否' }]}
  53 + />
39 54 <Search
40 55 allowClear
41 56 placeholder="库位号搜索"
... ...
src/pages/vms/OperationAdministration/components/PlayBackTable/index.tsx
... ... @@ -32,9 +32,9 @@ const columns: ColumnsType&lt;DataType&gt; = [
32 32 width: 100,
33 33 },
34 34 {
35   - title: '轨迹id',
36   - dataIndex: 'trid',
37   - key: 'trid',
  35 + title: '车牌号',
  36 + dataIndex: 'plateNumber',
  37 + key: 'plateNumber',
38 38 width: 100,
39 39 },
40 40 {
... ... @@ -86,7 +86,6 @@ function PlayBackTable(props: any) {
86 86 };
87 87  
88 88 const handlePageChange = (current: number, pageSize: number, keyword?: string) => {
89   - console.log(1);
90 89 setTableData([]);
91 90 setData({ ...data, current, pageSize, keyword });
92 91 };
... ... @@ -98,7 +97,7 @@ function PlayBackTable(props: any) {
98 97 enterButton="查询"
99 98 style={{ marginBottom: 10 }}
100 99 onSearch={(v) => handlePageChange(data.current || 1, data.pageSize || 10, v)}
101   - placeholder="搜索GPS号"
  100 + placeholder="搜索GPS号/车牌号"
102 101 />
103 102 <Table
104 103 dataSource={tableData}
... ...