Commit 28f41719ed20868d229dad0ef95907ffc1f7f7cc
Merge branch 'master' into contract
Showing
68 changed files
with
2110 additions
and
248 deletions
config/routers/crm_new.ts
... | ... | @@ -82,7 +82,11 @@ export default [ |
82 | 82 | component: './crm_new/Settings/subpages/CloseClue', |
83 | 83 | }, |
84 | 84 | { |
85 | + path: '/crm/clueConnect', // 线索有效接通配置 | |
86 | + component: './crm_new/CluesConnectTargetEffectively', | |
87 | + }, | |
88 | + { | |
85 | 89 | path: '/crm/clueShareBrand', // 线索品牌共享分组 |
86 | 90 | component: './crm_new/Settings/subpages/ClueBrandSharing', |
87 | - }, | |
91 | + } | |
88 | 92 | ]; |
89 | 93 | \ No newline at end of file | ... | ... |
config/routers/pms.ts
... | ... | @@ -163,4 +163,8 @@ export default [ |
163 | 163 | path: '/pms/transfer/storageOverStock', // 可调出配件 |
164 | 164 | component: './pms/transfer/StorageOverStock' |
165 | 165 | }, |
166 | + { | |
167 | + path: '/pms/transfer/huolalaSetting', // 货拉拉账户配置 | |
168 | + component: './pms/transfer/HuolalaSetting' | |
169 | + }, | |
166 | 170 | ]; | ... | ... |
src/components/CarTableTreeAuth/SelectSpec.tsx
0 → 100644
1 | +import React, { useEffect, useState } from "react"; | |
2 | +import { | |
3 | + Table, | |
4 | + Select, | |
5 | +} from "antd"; | |
6 | +import { | |
7 | + CarOptionVo, | |
8 | +} from "@/pages/stock/Components/api"; | |
9 | +import { ColumnsType } from 'antd/lib/table'; | |
10 | + | |
11 | +interface Props { | |
12 | + onChange?: (pa: any) => void; | |
13 | + value?: any[]; | |
14 | + disabled?: boolean; | |
15 | + specList?: CarOptionVo[] | |
16 | +} | |
17 | + | |
18 | +interface Item { | |
19 | + label: string; | |
20 | + value: number; | |
21 | + key?: number; | |
22 | +} | |
23 | +const { Column } = Table; | |
24 | +const { Option } = Select; | |
25 | + | |
26 | +const columns: ColumnsType<CarOptionVo> = [ | |
27 | + { | |
28 | + title: '车型', | |
29 | + dataIndex: 'name', | |
30 | + }, | |
31 | + { | |
32 | + title: '配置代码', | |
33 | + dataIndex: 'specConfigCodeList', | |
34 | + render: (t) => t.map((i: string, key: number) => (<div key={i}>{key + 1}.{i}</div>)) | |
35 | + }, | |
36 | +]; | |
37 | +export default function index({ onChange: onSelected, value, disabled, specList = [] }: Props) { | |
38 | + const rowSelection = { | |
39 | + onChange: (selectedRowKeys: React.Key[], selectedRows: CarOptionVo[]) => { | |
40 | + onSelected && onSelected(selectedRows); | |
41 | + }, | |
42 | + }; | |
43 | + | |
44 | + return ( | |
45 | + <> | |
46 | + <Table | |
47 | + rowSelection={{ | |
48 | + type: "checkbox", | |
49 | + selectedRowKeys: value && value.map(item => item.id), | |
50 | + ...rowSelection, | |
51 | + }} | |
52 | + pagination={false} | |
53 | + // loading={} | |
54 | + rowKey="id" | |
55 | + size="small" | |
56 | + scroll={{ y: 500 }} | |
57 | + columns={columns} | |
58 | + dataSource={specList} | |
59 | + /> | |
60 | + </> | |
61 | + ); | |
62 | +} | ... | ... |
src/components/CarTableTreeAuth/entity.ts
0 → 100644
1 | +export interface CarAuthList { | |
2 | + authType: number; | |
3 | + brandId: number; | |
4 | + brandName: number; | |
5 | + children?: Array<SeriesAuth> | |
6 | +} | |
7 | + | |
8 | +interface SeriesAuth { | |
9 | + authType: number; | |
10 | + seriesId: number; | |
11 | + seriesName: string; | |
12 | + children?: Array<SpecAuth> | |
13 | +} | |
14 | +interface SpecAuth { | |
15 | + authType: number; | |
16 | + specId: number; | |
17 | + specName: string; | |
18 | +} | |
0 | 19 | \ No newline at end of file | ... | ... |
src/components/CarTableTreeAuth/index.tsx
0 → 100644
1 | +import React, { useEffect, useState } from "react"; | |
2 | +import { | |
3 | + Button, | |
4 | + Card, | |
5 | + Table, | |
6 | + Modal, | |
7 | + Form, | |
8 | + Select, | |
9 | + Space, | |
10 | + Spin, | |
11 | + message, | |
12 | +} from "antd"; | |
13 | +import { | |
14 | + CarOptionVo, | |
15 | + getOnsaleSeriesApi, | |
16 | + getOnsaleSpecApi, | |
17 | +} from "@/pages/stock/Components/api"; | |
18 | +import { PlusOutlined } from '@ant-design/icons'; | |
19 | +import { ColumnsType } from 'antd/lib/table'; | |
20 | +import SelectSpec from './SelectSpec'; | |
21 | +import { CarAuthList } from './entity'; | |
22 | +import { getBrandFilterApi } from '@/common/api'; | |
23 | + | |
24 | +interface Props { | |
25 | + onChange?: (pa: any) => void; | |
26 | + value?: CarAuthList[]; | |
27 | + disabled?: boolean; | |
28 | + brandList?: CommonApi.OptionVO[]; | |
29 | + /**品牌是否多选,默认true */ | |
30 | + brandMultiple?: boolean; | |
31 | +} | |
32 | + | |
33 | +interface Item { | |
34 | + label: string; | |
35 | + value: number; | |
36 | + key?: number; | |
37 | +} | |
38 | +const { Column } = Table; | |
39 | +const { Option } = Select; | |
40 | + | |
41 | +export default function index({ onChange, value, disabled, brandMultiple = true, brandList }: Props) { | |
42 | + const [form] = Form.useForm(); | |
43 | + // 控制Modal是否可见 | |
44 | + const [visible, setVisible] = useState<boolean>(false); | |
45 | + // 控制是否可见车型下拉框 | |
46 | + const [level, setLevel] = useState<number>(1); | |
47 | + //存储表格当前车系 | |
48 | + const [currentItem, setCurrentItem] = useState<any>(); | |
49 | + const [brandData, setBrandData] = useState<CommonApi.OptionVO[]>([]); | |
50 | + //存储Modal中可选的车系 | |
51 | + const [series, setSeries] = useState<any>([]); | |
52 | + //存储接口返回的车型 | |
53 | + const [specList, setSpecList] = useState<any>([]); | |
54 | + //存储Modal选择的品牌和车系作为表格的数据源 | |
55 | + const [savaData, setSavadata] = useState<any>([]); | |
56 | + // 存储已经选中的车系 | |
57 | + const [selectedSeries, setSelectedSeries] = useState<any[]>([]); | |
58 | + const [fetchLoading, setFetchLoading] = useState(false); | |
59 | + | |
60 | + useEffect(() => { | |
61 | + if (value && value.length) { | |
62 | + setSelectedSeries([...value]); | |
63 | + setSavadata([...value]); | |
64 | + } | |
65 | + }, [value]); | |
66 | + | |
67 | + useEffect(() => { | |
68 | + if (brandList && Array.isArray(brandList)) { | |
69 | + setBrandData(brandList || []); | |
70 | + } | |
71 | + }, [brandList]); | |
72 | + | |
73 | + function getBrands() { | |
74 | + if (brandData.length) { | |
75 | + return; | |
76 | + } | |
77 | + setFetchLoading(true); | |
78 | + getBrandFilterApi().then(res => { | |
79 | + setBrandData(res.data || []); | |
80 | + setFetchLoading(false); | |
81 | + }).catch(err => { | |
82 | + message.error(err.message); | |
83 | + setFetchLoading(false); | |
84 | + }); | |
85 | + } | |
86 | + | |
87 | + const _onOk = () => { | |
88 | + form | |
89 | + .validateFields() | |
90 | + .then((fileds) => { | |
91 | + if (level === 2) { | |
92 | + const { series } = fileds; | |
93 | + const _series = series.map((item: Item) => ({ | |
94 | + seriesId: item.value, | |
95 | + seriesName: item.label, | |
96 | + authType: 1, | |
97 | + })); | |
98 | + | |
99 | + const currentSeries = (savaData.filter((i: any) => i.brandId === currentItem.brandId)[0] || {}).children || []; | |
100 | + /**判断已有数据保留 */ | |
101 | + if (currentSeries.length) { | |
102 | + const selectedIds = currentSeries.map(i => i.seriesId); | |
103 | + let newAuthSeries: any[] = []; | |
104 | + _series.forEach((item: any) => { | |
105 | + if (selectedIds.includes(item.seriesId)) { | |
106 | + newAuthSeries = newAuthSeries.concat(currentSeries.filter((list: any) => list.seriesId === item.seriesId)); | |
107 | + } else { | |
108 | + newAuthSeries.push(item); | |
109 | + } | |
110 | + }); | |
111 | + currentItem.children = newAuthSeries; | |
112 | + } else { | |
113 | + currentItem.children = _series; | |
114 | + } | |
115 | + | |
116 | + const tempData = savaData.map((item: any) => (item.brandId === currentItem.brandId ? { ...currentItem, authType: series.length ? 2 : 1 } : { ...item, authType: 1 })); | |
117 | + setSavadata([...tempData]); | |
118 | + onChange && onChange(tempData); | |
119 | + setVisible(false); | |
120 | + return; | |
121 | + } | |
122 | + if (level === 3) { | |
123 | + const { spec } = fileds; | |
124 | + const _spec = spec.map((item: any) => ({ | |
125 | + specId: item.id, | |
126 | + specName: item.name, | |
127 | + })); | |
128 | + currentItem.children = _spec; | |
129 | + currentItem.authType = 2; | |
130 | + const tempData = savaData.map((item: any) => { | |
131 | + if (item.children) { | |
132 | + const newChildren = item.children.map((i: any) => (i.seriesId === currentItem.seriesId ? { ...currentItem, authType: spec.length ? 2 : 1 } : i)); | |
133 | + item.children = newChildren; | |
134 | + } | |
135 | + return { ...item, authType: 1 }; | |
136 | + }); | |
137 | + setSavadata([...tempData]); | |
138 | + onChange && onChange(tempData); | |
139 | + setVisible(false); | |
140 | + return; | |
141 | + } | |
142 | + const { brand } = fileds; | |
143 | + const tempArray = (brandMultiple ? brand : [brand]).map((item: any) => ({ | |
144 | + brandName: item.label, | |
145 | + brandId: item.value, | |
146 | + authType: 1, //1全部2部分 | |
147 | + })); | |
148 | + const selectedBrands: number[] = savaData.map((i: any) => i.brandId); | |
149 | + let newAuthCar: any[] = []; | |
150 | + tempArray.forEach((item: any) => { | |
151 | + if (selectedBrands.includes(item.brandId)) { | |
152 | + newAuthCar = newAuthCar.concat(savaData.filter(list => list.brandId === item.brandId)); | |
153 | + } else { | |
154 | + newAuthCar.push(item); | |
155 | + } | |
156 | + }); | |
157 | + setSavadata(newAuthCar); | |
158 | + onChange && onChange(newAuthCar); | |
159 | + setVisible(false); | |
160 | + }) | |
161 | + .catch((err) => console.log(err.message)); | |
162 | + }; | |
163 | + | |
164 | + // 选择部分车辆表格==》删除车系 | |
165 | + const onDelete = (record: any) => { | |
166 | + const _savaData = savaData.filter( | |
167 | + (item: any) => item.seriesId != record.seriesId | |
168 | + ); | |
169 | + const _selectedSeries = selectedSeries.filter( | |
170 | + (item) => item.seriesId != record.seriesId | |
171 | + ); | |
172 | + setSavadata([..._savaData]); | |
173 | + onChange && onChange(_savaData); | |
174 | + setSelectedSeries([..._selectedSeries]); | |
175 | + }; | |
176 | + | |
177 | + // 选择部分车辆表格==》编辑选择部分车系、车型 | |
178 | + const onSelectSpec = async (record: any) => { | |
179 | + setCurrentItem(record); | |
180 | + setVisible(true); | |
181 | + if (record.brandId) { | |
182 | + setLevel(2); | |
183 | + setFetchLoading(true); | |
184 | + try { | |
185 | + const { data } = await getOnsaleSeriesApi(record.brandId); | |
186 | + const selectedSeries = savaData.filter((brand: any) => brand.brandId === record.brandId)[0].children; | |
187 | + selectedSeries && form.setFieldValue("series", selectedSeries.map(i => ({ value: i.seriesId, label: i.seriesName }))); | |
188 | + setSeries(data); | |
189 | + setFetchLoading(false); | |
190 | + } catch (err: any) { | |
191 | + message.error(err.message); | |
192 | + setFetchLoading(false); | |
193 | + } | |
194 | + } | |
195 | + if (record.seriesId) { | |
196 | + setLevel(3); | |
197 | + setFetchLoading(true); | |
198 | + try { | |
199 | + const { data } = await getOnsaleSpecApi(record.seriesId); | |
200 | + record.children && form.setFieldValue("spec", record.children.map(i => ({ id: i.specId, name: i.specName }))); | |
201 | + setSpecList(data); | |
202 | + setFetchLoading(false); | |
203 | + } catch (err: any) { | |
204 | + message.error(err.message); | |
205 | + setFetchLoading(false); | |
206 | + } | |
207 | + } | |
208 | + }; | |
209 | + | |
210 | + return ( | |
211 | + <> | |
212 | + <Card> | |
213 | + {!disabled && ( | |
214 | + <div style={{ display: "flex", justifyContent: "flex-end" }}> | |
215 | + <Button | |
216 | + type="link" | |
217 | + disabled={disabled} | |
218 | + icon={<PlusOutlined />} | |
219 | + onClick={() => { | |
220 | + getBrands(); | |
221 | + setVisible(true); | |
222 | + setLevel(1); | |
223 | + savaData.length && form.setFieldValue("brand", savaData.map((i: any) => ({ value: i.brandId, label: i.brandName }))); | |
224 | + // form.resetFields(); | |
225 | + }} | |
226 | + > | |
227 | + 新增 | |
228 | + </Button> | |
229 | + </div> | |
230 | + )} | |
231 | + | |
232 | + <Table | |
233 | + dataSource={value} | |
234 | + rowKey={(record) => String(record.brandId || record.seriesId || record.specId)} | |
235 | + > | |
236 | + <Column | |
237 | + title="品牌" | |
238 | + dataIndex="brandName" | |
239 | + key="brandId" | |
240 | + /> | |
241 | + <Column | |
242 | + title="车系" | |
243 | + dataIndex="seriesName" | |
244 | + key="seriesId" | |
245 | + render={(text, record: any) => { | |
246 | + return (text || record.specName || (record.children && record.children.length !== 0) ? ( | |
247 | + <span> | |
248 | + {text} | |
249 | + </span> | |
250 | + ) : ( | |
251 | + <span style={{ color: "#999" }}>全部车系</span> | |
252 | + )); | |
253 | + }} | |
254 | + /> | |
255 | + <Column | |
256 | + title="车型" | |
257 | + dataIndex="specName" | |
258 | + key="specId" | |
259 | + render={(text, record: any) => { | |
260 | + return (text || (record.children && record.children.length !== 0) ? ( | |
261 | + <span> | |
262 | + {text} | |
263 | + </span> | |
264 | + ) : ( | |
265 | + <span style={{ color: "#999" }}>全部车型</span> | |
266 | + )); | |
267 | + }} | |
268 | + /> | |
269 | + {!disabled && ( | |
270 | + <Column | |
271 | + title="操作" | |
272 | + render={(_, record: any, index) => { | |
273 | + return ( | |
274 | + <Space> | |
275 | + <Button | |
276 | + type={record.brandId ? "primary" : "link"} | |
277 | + style={{ padding: 2 }} | |
278 | + onClick={() => onSelectSpec(record)} | |
279 | + disabled={disabled} | |
280 | + > | |
281 | + {record.brandId ? "编辑车系" : (record.seriesId ? "编辑车型" : "")} | |
282 | + </Button> | |
283 | + {/* <Popconfirm | |
284 | + title="确定删除?" | |
285 | + okText="确定" | |
286 | + style={{ padding: 0 }} | |
287 | + cancelText="取消" | |
288 | + onConfirm={() => onDelete(record)} | |
289 | + > | |
290 | + <Button type="link" danger disabled={disabled}> | |
291 | + 删除 | |
292 | + </Button> | |
293 | + </Popconfirm> */} | |
294 | + </Space> | |
295 | + ); | |
296 | + }} | |
297 | + /> | |
298 | + )} | |
299 | + </Table> | |
300 | + </Card> | |
301 | + | |
302 | + {/* 选择品牌和车系 */} | |
303 | + <Modal | |
304 | + title="选择车辆信息" | |
305 | + visible={visible} | |
306 | + onOk={() => form.submit()} | |
307 | + onCancel={() => { | |
308 | + setVisible(false); | |
309 | + }} | |
310 | + maskClosable={false} | |
311 | + afterClose={() => { | |
312 | + form.resetFields(); | |
313 | + setCurrentItem({}); | |
314 | + }} | |
315 | + > | |
316 | + <Spin spinning={fetchLoading}> | |
317 | + <Form onFinish={_onOk} form={form}> | |
318 | + {level === 1 ? ( | |
319 | + <Form.Item | |
320 | + label="品牌" | |
321 | + name="brand" | |
322 | + > | |
323 | + <Select | |
324 | + labelInValue | |
325 | + mode={brandMultiple ? "multiple" : undefined} | |
326 | + placeholder="选择品牌" | |
327 | + > | |
328 | + {brandData.map((item) => ( | |
329 | + <Option value={item.id} key={item.id}> | |
330 | + {item.name} | |
331 | + </Option> | |
332 | + ))} | |
333 | + </Select> | |
334 | + </Form.Item> | |
335 | + ) : null} | |
336 | + | |
337 | + {/* 车系 */} | |
338 | + {level === 2 && ( | |
339 | + <Form.Item | |
340 | + label="车系" | |
341 | + name="series" | |
342 | + // rules={[{ required: true, message: "请选择车系" }]} | |
343 | + > | |
344 | + <Select labelInValue mode="multiple" placeholder="选择车系" allowClear> | |
345 | + {series.map((item: any) => ( | |
346 | + <Option value={item.id} key={item.id}> | |
347 | + {item.name} | |
348 | + </Option> | |
349 | + ))} | |
350 | + </Select> | |
351 | + </Form.Item> | |
352 | + )} | |
353 | + {level === 3 ? ( | |
354 | + <Form.Item | |
355 | + label="" | |
356 | + name="spec" | |
357 | + > | |
358 | + {/* <Select labelInValue mode="multiple" placeholder="选择车型" allowClear> | |
359 | + {specList.map((item: any) => ( | |
360 | + <Option value={item.id} key={item.id}> | |
361 | + {item.name} | |
362 | + </Option> | |
363 | + ))} | |
364 | + </Select> */} | |
365 | + <SelectSpec specList={specList} /> | |
366 | + </Form.Item> | |
367 | + ) : null} | |
368 | + </Form> | |
369 | + </Spin> | |
370 | + </Modal> | |
371 | + </> | |
372 | + ); | |
373 | +} | ... | ... |
src/pages/attendance/Attend/subpages/AttendanceGroup/api.ts
1 | -import { http,PromisePageResp } from "@/typing/http"; | |
1 | +import { http, PromisePageResp } from "@/typing/http"; | |
2 | 2 | import request from "@/utils/request"; |
3 | 3 | import { ATTENDANCE_HOST } from "@/utils/host"; |
4 | 4 | |
... | ... | @@ -70,10 +70,15 @@ export function saveApi(params?: Item): P<void> { |
70 | 70 | } |
71 | 71 | |
72 | 72 | interface BaseSettingItem { |
73 | - id?: number; | |
74 | - noticeTime?: number; //待办提前通知时间(分钟) | |
75 | - defectTime?: number; //严重迟到时间判定(分钟) | |
76 | - lackTime?: number; //缺卡判定时间 | |
73 | + id?: number; // 主键 | |
74 | + groupId?: number; // 集团id | |
75 | + defectTime?: number; // 严重迟到时间判定(分钟) | |
76 | + onDutyNoticeTime?: number; // 上班待办提前通知时间(分钟) | |
77 | + offDutyNoticeTime?: number; // 下班待办延后通知时间(分钟) | |
78 | + onDutyLackTime?: number; // 上班缺卡时间判定(分钟) | |
79 | + offDutyLackTime?: number; // 下班缺卡时间判定(分钟) | |
80 | + outsideRemainingHour?: number; // 外勤结束时间离门店下班时间≤?小时不用打考勤卡 | |
81 | + travelRemainingHour?: number; // 出差结束时间离门店下班时间≤?小时不用打考勤卡 | |
77 | 82 | } |
78 | 83 | /**获取考勤组基础设置 |
79 | 84 | * /attendance/base/setting/get | ... | ... |
src/pages/attendance/Attend/subpages/AttendanceGroup/components/TimeConfig.tsx
1 | -import { Button, Form, InputNumber, message, Popconfirm, Radio, Row, Select } from "antd"; | |
1 | +import { Button, Form, InputNumber, message, Popconfirm, Row } from "antd"; | |
2 | 2 | import React, { useEffect, useState } from "react"; |
3 | 3 | import useInitial from "@/hooks/useInitail"; |
4 | -import { fetchBaseSettingApi, saveOrUpdateApi } from "@/pages/attendance/Attend/subpages/AttendanceGroup/api"; | |
4 | +import { | |
5 | + fetchBaseSettingApi, | |
6 | + saveOrUpdateApi, | |
7 | +} from "@/pages/attendance/Attend/subpages/AttendanceGroup/api"; | |
5 | 8 | |
6 | 9 | const FormItem = Form.Item; |
7 | 10 | |
... | ... | @@ -49,7 +52,12 @@ export default function TimeConfig() { |
49 | 52 | |
50 | 53 | return ( |
51 | 54 | <div> |
52 | - <Form form={form} onFinish={submit} labelCol={{ span: 6 }} wrapperCol={{ span: 15 }}> | |
55 | + <Form | |
56 | + form={form} | |
57 | + onFinish={submit} | |
58 | + labelCol={{ span: 6 }} | |
59 | + wrapperCol={{ span: 15 }} | |
60 | + > | |
53 | 61 | <div |
54 | 62 | style={{ |
55 | 63 | display: "flex", |
... | ... | @@ -77,7 +85,8 @@ export default function TimeConfig() { |
77 | 85 | /> |
78 | 86 | </FormItem> |
79 | 87 | <span style={{ marginLeft: 5 }}> |
80 | - 到应打卡时间 <span style={{ color: "#999999" }}>(除最后一次应打卡)</span> | |
88 | + 到应打卡时间{" "} | |
89 | + <span style={{ color: "#999999" }}>(除最后一次应打卡)</span> | |
81 | 90 | </span> |
82 | 91 | </div> |
83 | 92 | </div> |
... | ... | @@ -93,7 +102,10 @@ export default function TimeConfig() { |
93 | 102 | > |
94 | 103 | <span style={{ color: "#ff4d4f" }}>*</span> 最后一次应打卡时间要求: |
95 | 104 | <div> |
96 | - <span style={{ marginRight: 5, marginLeft: 5 }}> 应打卡时间延后</span> | |
105 | + <span style={{ marginRight: 5, marginLeft: 5 }}> | |
106 | + {" "} | |
107 | + 应打卡时间延后 | |
108 | + </span> | |
97 | 109 | <FormItem |
98 | 110 | name="offDutyNoticeTime" |
99 | 111 | label="打卡时间要求" |
... | ... | @@ -108,7 +120,9 @@ export default function TimeConfig() { |
108 | 120 | disabled={!edit} |
109 | 121 | /> |
110 | 122 | </FormItem> |
111 | - <span style={{ color: "#999999", marginLeft: 5 }}>(当天仅一次打卡不计此情况)</span> | |
123 | + <span style={{ color: "#999999", marginLeft: 5 }}> | |
124 | + (当天仅一次打卡不计此情况) | |
125 | + </span> | |
112 | 126 | </div> |
113 | 127 | </div> |
114 | 128 | |
... | ... | @@ -123,9 +137,17 @@ export default function TimeConfig() { |
123 | 137 | > |
124 | 138 | <span style={{ color: "#ff4d4f" }}>*</span> 严重迟到判定: |
125 | 139 | <div> |
126 | - <span style={{ marginLeft: 5, marginRight: 5 }}>在应打卡时间延后</span> | |
140 | + <span style={{ marginLeft: 5, marginRight: 5 }}> | |
141 | + 在应打卡时间延后 | |
142 | + </span> | |
127 | 143 | |
128 | - <FormItem name="defectTime" noStyle rules={[{ required: true, message: "请填写严重迟到时间判定(分钟)" }]}> | |
144 | + <FormItem | |
145 | + name="defectTime" | |
146 | + noStyle | |
147 | + rules={[ | |
148 | + { required: true, message: "请填写严重迟到时间判定(分钟)" }, | |
149 | + ]} | |
150 | + > | |
129 | 151 | <InputNumber |
130 | 152 | min={0} |
131 | 153 | style={{ width: 200 }} |
... | ... | @@ -147,7 +169,10 @@ export default function TimeConfig() { |
147 | 169 | > |
148 | 170 | <span style={{ color: "#ff4d4f" }}>*</span> 上班缺卡时间判定: |
149 | 171 | <div> |
150 | - <span style={{ marginRight: 5, marginLeft: 5 }}> 应打卡时间延后</span> | |
172 | + <span style={{ marginRight: 5, marginLeft: 5 }}> | |
173 | + {" "} | |
174 | + 应打卡时间延后 | |
175 | + </span> | |
151 | 176 | <FormItem |
152 | 177 | name="onDutyLackTime" |
153 | 178 | label="打卡时间要求" |
... | ... | @@ -175,8 +200,14 @@ export default function TimeConfig() { |
175 | 200 | > |
176 | 201 | <span style={{ color: "#ff4d4f" }}>*</span> 下班缺卡时间判定: |
177 | 202 | <div> |
178 | - <span style={{ marginRight: 5, marginLeft: 5 }}>在应打卡时间延后</span> | |
179 | - <FormItem name="offDutyLackTime" noStyle rules={[{ required: true, message: "请填写缺卡时间判定(分钟)" }]}> | |
203 | + <span style={{ marginRight: 5, marginLeft: 5 }}> | |
204 | + 在应打卡时间延后 | |
205 | + </span> | |
206 | + <FormItem | |
207 | + name="offDutyLackTime" | |
208 | + noStyle | |
209 | + rules={[{ required: true, message: "请填写缺卡时间判定(分钟)" }]} | |
210 | + > | |
180 | 211 | <InputNumber |
181 | 212 | min={0} |
182 | 213 | style={{ width: 200 }} |
... | ... | @@ -188,6 +219,66 @@ export default function TimeConfig() { |
188 | 219 | <span style={{ marginLeft: 5 }}>后不允许打卡</span> |
189 | 220 | </div> |
190 | 221 | </div> |
222 | + <div | |
223 | + style={{ | |
224 | + display: "flex", | |
225 | + justifyContent: "flex-start", | |
226 | + alignItems: "center", | |
227 | + marginTop: 20, | |
228 | + paddingLeft: "20%", | |
229 | + }} | |
230 | + > | |
231 | + <span style={{ color: "#ff4d4f" }}>*</span> 外勤结束时间: | |
232 | + <div> | |
233 | + <span style={{ marginLeft: 5, marginRight: 5 }}> | |
234 | + 离门店下班时间提前 | |
235 | + </span> | |
236 | + <FormItem | |
237 | + name="outsideRemainingHour" | |
238 | + noStyle | |
239 | + rules={[{ required: true, message: "请填写打卡时间要求" }]} | |
240 | + > | |
241 | + <InputNumber | |
242 | + min={0} | |
243 | + style={{ width: 200 }} | |
244 | + formatter={(value) => `${value}小时`} | |
245 | + parser={(value: any) => value.replace("小时", "")} | |
246 | + disabled={!edit} | |
247 | + /> | |
248 | + </FormItem> | |
249 | + <span style={{ marginLeft: 5, marginRight: 5 }}>不用考勤打卡</span> | |
250 | + </div> | |
251 | + </div> | |
252 | + <div | |
253 | + style={{ | |
254 | + display: "flex", | |
255 | + justifyContent: "flex-start", | |
256 | + alignItems: "center", | |
257 | + marginTop: 20, | |
258 | + paddingLeft: "20%", | |
259 | + }} | |
260 | + > | |
261 | + <span style={{ color: "#ff4d4f" }}>*</span> 出差结束时间: | |
262 | + <div> | |
263 | + <span style={{ marginLeft: 5, marginRight: 5 }}> | |
264 | + 离门店下班时间提前 | |
265 | + </span> | |
266 | + <FormItem | |
267 | + name="travelRemainingHour" | |
268 | + noStyle | |
269 | + rules={[{ required: true, message: "请填写打卡时间要求" }]} | |
270 | + > | |
271 | + <InputNumber | |
272 | + min={0} | |
273 | + style={{ width: 200 }} | |
274 | + formatter={(value) => `${value}小时`} | |
275 | + parser={(value: any) => value.replace("小时", "")} | |
276 | + disabled={!edit} | |
277 | + /> | |
278 | + </FormItem> | |
279 | + <span style={{ marginLeft: 5, marginRight: 5 }}>不用考勤打卡</span> | |
280 | + </div> | |
281 | + </div> | |
191 | 282 | </Form> |
192 | 283 | <Row style={{ width: "80%", marginTop: 20 }} justify="center"> |
193 | 284 | {!edit ? ( |
... | ... | @@ -196,7 +287,11 @@ export default function TimeConfig() { |
196 | 287 | </Button> |
197 | 288 | ) : ( |
198 | 289 | <div> |
199 | - <Button type="primary" onClick={form.submit} loading={confirmLoading}> | |
290 | + <Button | |
291 | + type="primary" | |
292 | + onClick={form.submit} | |
293 | + loading={confirmLoading} | |
294 | + > | |
200 | 295 | 确定 |
201 | 296 | </Button> |
202 | 297 | <Popconfirm | ... | ... |
src/pages/attendance/Leave/components/Modal.tsx
... | ... | @@ -22,8 +22,9 @@ const CreateModal = (props: Props) => { |
22 | 22 | useEffect(() => { |
23 | 23 | if (visiable && type === 1) { |
24 | 24 | API.fetchDetail(1).then((res: any) => { |
25 | - setYear(res.data); | |
26 | - setYearData(res.data.conditions); | |
25 | + const data = res.data || {}; | |
26 | + setYear(data); | |
27 | + setYearData(data.conditions); | |
27 | 28 | }); |
28 | 29 | } |
29 | 30 | }, [visiable]); |
... | ... | @@ -91,7 +92,7 @@ const CreateModal = (props: Props) => { |
91 | 92 | rules={[{ required: !(type === 1 || type === 8 || type === 6) }]} |
92 | 93 | hidden={type === 1 || type === 8 || type === 6 || type === 10} |
93 | 94 | > |
94 | - <InputNumber min={1} style={{ width: 200 }} /> | |
95 | + <InputNumber min={1} style={{ width: 200 }} addonAfter="天" /> | |
95 | 96 | </FormItem> |
96 | 97 | <FormItem |
97 | 98 | name="annualWay" |
... | ... | @@ -110,7 +111,7 @@ const CreateModal = (props: Props) => { |
110 | 111 | rules={[{ required: type === 1 }]} |
111 | 112 | hidden={!(type === 1)} |
112 | 113 | > |
113 | - <InputNumber min={1} style={{ width: 200 }} /> | |
114 | + <InputNumber min={1} style={{ width: 200 }} addonAfter="天" /> | |
114 | 115 | </FormItem> |
115 | 116 | <FormItem |
116 | 117 | name="advanceDays" |
... | ... | @@ -127,7 +128,7 @@ const CreateModal = (props: Props) => { |
127 | 128 | ]} |
128 | 129 | hidden={type === 3 || type === 8 || type === 5 || type === 10} |
129 | 130 | > |
130 | - <InputNumber min={1} style={{ width: 200 }} /> | |
131 | + <InputNumber min={1} style={{ width: 200 }} addonAfter="天" /> | |
131 | 132 | </FormItem> |
132 | 133 | <FormItem |
133 | 134 | name="times" |
... | ... | @@ -135,7 +136,7 @@ const CreateModal = (props: Props) => { |
135 | 136 | rules={[{ required: type === 6 || type === 8 }]} |
136 | 137 | hidden={!(type === 6 || type === 8)} |
137 | 138 | > |
138 | - <InputNumber min={1} style={{ width: 200 }} /> | |
139 | + <InputNumber min={1} style={{ width: 200 }} addonAfter="次" /> | |
139 | 140 | </FormItem> |
140 | 141 | <FormItem |
141 | 142 | name="conditions" | ... | ... |
src/pages/attendance/Leave/index.tsx
1 | -import React, { useState } from 'react'; | |
1 | +import React, { useState } from "react"; | |
2 | 2 | import { PageHeaderWrapper } from "@ant-design/pro-layout"; |
3 | -import { Button, Card, Table } from 'antd'; | |
4 | -import * as TYPE from './entity'; | |
5 | -import Modal from './components/Modal'; | |
6 | -import useInitial from '@/hooks/useInitail'; | |
7 | -import * as API from './api'; | |
3 | +import { Button, Card, Table } from "antd"; | |
4 | +import * as TYPE from "./entity"; | |
5 | +import Modal from "./components/Modal"; | |
6 | +import useInitial from "@/hooks/useInitail"; | |
7 | +import * as API from "./api"; | |
8 | 8 | |
9 | 9 | const Column = Table.Column; |
10 | 10 | |
... | ... | @@ -13,7 +13,7 @@ const Leave = () => { |
13 | 13 | const [type, setType] = useState<number>(0); |
14 | 14 | const { data, setParams } = useInitial(API.fetchDetail, {}, type); |
15 | 15 | |
16 | - const onEdit = (record:any) => { | |
16 | + const onEdit = (record: any) => { | |
17 | 17 | setParams(record.type, true); |
18 | 18 | setType(record.type); |
19 | 19 | setVisiable(true); |
... | ... | @@ -40,10 +40,10 @@ const Leave = () => { |
40 | 40 | visiable={visiable} |
41 | 41 | setVisiable={setVisiable} |
42 | 42 | type={type} |
43 | - data={data} | |
43 | + data={data || {}} | |
44 | 44 | /> |
45 | 45 | </PageHeaderWrapper> |
46 | 46 | ); |
47 | 47 | }; |
48 | 48 | |
49 | -export default Leave; | |
50 | 49 | \ No newline at end of file |
50 | +export default Leave; | ... | ... |
src/pages/crm_new/CluesConnectTargetEffectively/api.ts
0 → 100644
1 | +import request from '@/utils/request'; | |
2 | +import { CRM_HOST } from '@/utils/host'; | |
3 | + | |
4 | +interface RequestParams { | |
5 | + keywords?: string | |
6 | +} | |
7 | + | |
8 | +export interface Result { | |
9 | + id?: number // 配置id | |
10 | + dialAims?: number // 线索接通目标 | |
11 | + displayName?: string // 显示名称 | |
12 | + shopList?: ShopList[] // 门店列表 | |
13 | +} | |
14 | + | |
15 | +export interface ShopList { | |
16 | + shopId?: number // 门店id | |
17 | + shopName?: string // 门店名称 | |
18 | +} | |
19 | + | |
20 | +/** 查询线索拨通目标配置列表 */ | |
21 | +export function getConfigApi(params: RequestParams) { | |
22 | + return request.get<Result[]>(`${CRM_HOST}/erp/clue/dial/aims/config/list`, {params}); | |
23 | +} | |
24 | + | |
25 | +/** 查询线索拨通目标已配置的门店 */ | |
26 | +export function getHaveShopListApi() { | |
27 | + return request.get<number[]>(`${CRM_HOST}/erp/clue/dial/aims/config/already/exists/shopIds`); | |
28 | +} | |
29 | + | |
30 | +/** 保存线索拨通目标配置 */ | |
31 | +export function saveConfigApi(params: Result) { | |
32 | + return request.post<Result>(`${CRM_HOST}/erp/clue/dial/aims/config/save`, params); | |
33 | +} | |
34 | + | |
35 | +/** 删除线索拨通目标配置 */ | |
36 | +export function deleteConfigApi(id?: number) { | |
37 | + return request.post<Result>(`${CRM_HOST}/erp/clue/dial/aims/config/delete`, {id}, {contentType: 'form-urlencoded'}); | |
38 | +} | |
0 | 39 | \ No newline at end of file | ... | ... |
src/pages/crm_new/CluesConnectTargetEffectively/components/EditModal.tsx
0 → 100644
1 | +import React, { useState, useEffect } from 'react'; | |
2 | +import { Button, Form, message, InputNumber, Select, Modal} from 'antd'; | |
3 | +import ShopSelectNew from '@/components/ShopSelectNew'; | |
4 | +import { useStore } from '../index'; | |
5 | +import { saveConfigApi, getHaveShopListApi } from '../api'; | |
6 | +import { debounce } from 'lodash'; | |
7 | + | |
8 | +const Option = Select.Option; | |
9 | + | |
10 | +export default function Index() { | |
11 | + const [form] = Form.useForm(); | |
12 | + const { current, setCurrent, setLoading } = useStore(); | |
13 | + const [confirm, setConfirm] = useState<boolean>(false); | |
14 | + const [disabledShopIds, setDisabledShopIds] = useState<number[]>([]); | |
15 | + | |
16 | + useEffect(() => { | |
17 | + if (current.visible && current.data.id) { | |
18 | + handleSetValue(); | |
19 | + const shopList = current.data.shopList || []; | |
20 | + getHaveShopListApi() | |
21 | + .then(res => { | |
22 | + const shopIdList = res.data || []; | |
23 | + const _shopIds = shopList.map(v => v.shopId) || []; | |
24 | + const disabledShop = shopIdList.filter(v => !(_shopIds.includes(v))) || []; | |
25 | + setDisabledShopIds(disabledShop); | |
26 | + }) | |
27 | + .catch(e => { | |
28 | + message.error(e.message); | |
29 | + }); | |
30 | + } else { | |
31 | + getHaveShopListApi() | |
32 | + .then(res => { | |
33 | + const shopIdList = res.data || []; | |
34 | + setDisabledShopIds(shopIdList); | |
35 | + }) | |
36 | + .catch(e => { | |
37 | + message.error(e.message); | |
38 | + }); | |
39 | + } | |
40 | + }, [current.visible]); | |
41 | + | |
42 | + function handleCancle() { | |
43 | + setCurrent({visible: false, data: {}}); | |
44 | + } | |
45 | + | |
46 | + async function handleSubmit() { | |
47 | + const params = await form.validateFields(); | |
48 | + setConfirm(true); | |
49 | + const _params = { | |
50 | + id: current.data?.id, | |
51 | + dialAims: params.dialAims, | |
52 | + shopList: params.shopList?.map((v: any) => ({shopId: v.value, shopName: v.label})) | |
53 | + }; | |
54 | + saveConfigApi(_params) | |
55 | + .then(res => { | |
56 | + message.success(res.result); | |
57 | + setConfirm(false); | |
58 | + setCurrent({visible: false, data: {}}); | |
59 | + setLoading(true); | |
60 | + }) | |
61 | + .catch(e => { | |
62 | + message.error(e.message); | |
63 | + setConfirm(false); | |
64 | + }); | |
65 | + } | |
66 | + | |
67 | + function handleSetValue() { | |
68 | + form.setFieldsValue({ | |
69 | + dialAims: current.data.dialAims, | |
70 | + shopList: current.data.shopList?.map((v: any) => ({label: v.shopName, value: v.shopId})) | |
71 | + }); | |
72 | + } | |
73 | + | |
74 | + function handleResetValue() { | |
75 | + form.setFieldsValue({shopList: [], dialAims: undefined}); | |
76 | + } | |
77 | + return ( | |
78 | + <Modal | |
79 | + title={current.data.id ? "编辑" : "新增"} | |
80 | + destroyOnClose | |
81 | + visible={current.visible} | |
82 | + maskClosable={false} | |
83 | + onCancel={handleCancle} | |
84 | + afterClose={() => handleResetValue()} | |
85 | + width="60%" | |
86 | + footer={[ | |
87 | + <Button key="cancel" disabled={confirm} onClick={handleCancle} style={{marginLeft: 10}}>取消</Button>, | |
88 | + <Button key="submit" disabled={confirm} onClick={debounce(handleSubmit, 380)} type="primary" htmlType="submit" loading={confirm}>确认</Button> | |
89 | + ]} | |
90 | + > | |
91 | + <Form | |
92 | + form={form} | |
93 | + labelCol={{ span: 8 }} | |
94 | + wrapperCol={{ span: 10 }} | |
95 | + > | |
96 | + | |
97 | + <Form.Item | |
98 | + label="线索有效接通目标" | |
99 | + name="dialAims" | |
100 | + rules={[{ required: true, message: "请输入" }]} | |
101 | + > | |
102 | + <InputNumber | |
103 | + style={{width: '100%'}} | |
104 | + controls={false} | |
105 | + min={0} | |
106 | + formatter={value => `${value}条/天`} | |
107 | + precision={0} | |
108 | + parser={value => value?.replace('条/天', '')} | |
109 | + /> | |
110 | + </Form.Item> | |
111 | + <Form.Item | |
112 | + label="适用门店" | |
113 | + name="shopList" | |
114 | + rules={[{ required: true, message: "请选择门店" }]} | |
115 | + labelAlign="right" | |
116 | + > | |
117 | + <ShopSelectNew disabledShopIds={disabledShopIds} defaultOptions={{bizTypes: "1"}} placeholder="请选择门店" multiple /> | |
118 | + </Form.Item> | |
119 | + | |
120 | + </Form> | |
121 | + | |
122 | + </Modal> | |
123 | + ); | |
124 | +} | |
0 | 125 | \ No newline at end of file | ... | ... |
src/pages/crm_new/CluesConnectTargetEffectively/components/List.tsx
0 → 100644
1 | +import React from "react"; | |
2 | +import { message, Popconfirm, Table, Space } from "antd"; | |
3 | +import { useStore } from '../index'; | |
4 | +import {deleteConfigApi, Result } from '../api'; | |
5 | + | |
6 | +const Column = Table.Column; | |
7 | + | |
8 | +export default function TableList() { | |
9 | + const {data, loading, setLoading, setShopData, setCurrent } = useStore(); | |
10 | + | |
11 | + function handleDelete(id?: number) { | |
12 | + deleteConfigApi(id) | |
13 | + .then(res => { | |
14 | + message.success(res.result); | |
15 | + setLoading(true); | |
16 | + }) | |
17 | + .catch(e => { | |
18 | + message.error(e.message); | |
19 | + }); | |
20 | + } | |
21 | + | |
22 | + return ( | |
23 | + <div> | |
24 | + <Table | |
25 | + dataSource={data} | |
26 | + pagination={false} | |
27 | + loading={loading} | |
28 | + rowKey="id" | |
29 | + > | |
30 | + <Column | |
31 | + title="上班期间线索有效接通目标(条/天)" | |
32 | + align="left" | |
33 | + dataIndex="dialAims" | |
34 | + /> | |
35 | + <Column | |
36 | + title="适用门店" | |
37 | + align="left" | |
38 | + dataIndex="displayName" | |
39 | + render={(_text: string, record: Result) => <span style={{color: "#4189FD"}} onClick={() => setShopData({visible: true, data: record.shopList || []})}>{_text}</span>} | |
40 | + /> | |
41 | + <Column | |
42 | + title="操作" | |
43 | + align="left" | |
44 | + render={(_text, record: Result) => { | |
45 | + return ( | |
46 | + <Space> | |
47 | + <a onClick={() => setCurrent({visible: true, data: record})} style={{ display: "block", color: "#4189FD" }}>编辑</a> | |
48 | + <Popconfirm | |
49 | + title="是否删除?" | |
50 | + okText="确定" | |
51 | + cancelText="取消" | |
52 | + onConfirm={() => handleDelete(record.id)} | |
53 | + > | |
54 | + <a style={{color: "#EC3F2F"}}>删除</a> | |
55 | + </Popconfirm> | |
56 | + </Space> | |
57 | + ); | |
58 | + }} | |
59 | + /> | |
60 | + </Table> | |
61 | + </div> | |
62 | + ); | |
63 | +} | ... | ... |
src/pages/crm_new/CluesConnectTargetEffectively/components/ShopModal.tsx
0 → 100644
1 | +import React, {useEffect, useState} from 'react'; | |
2 | +import {Modal, Button, List} from 'antd'; | |
3 | +import { useStore } from '../index'; | |
4 | + | |
5 | +export default function Index() { | |
6 | + const {shopData, setShopData } = useStore(); | |
7 | + | |
8 | + function handleCancel() { | |
9 | + setShopData({visible: false, data: []}); | |
10 | + } | |
11 | + | |
12 | + return ( | |
13 | + <Modal | |
14 | + destroyOnClose | |
15 | + title="适用门店" | |
16 | + visible={shopData.visible} | |
17 | + maskClosable={false} | |
18 | + onCancel={handleCancel} | |
19 | + footer={[]} | |
20 | + > | |
21 | + <List | |
22 | + size="large" | |
23 | + style={{height: '300px', overflow: 'scroll'}} | |
24 | + dataSource={shopData?.data || []} | |
25 | + renderItem={(item: any) => <List.Item style={{justifyContent: 'center'}}>{item.shopName}</List.Item>} | |
26 | + /> | |
27 | + <div | |
28 | + style={{ | |
29 | + textAlign: 'center', | |
30 | + marginTop: 12, | |
31 | + height: 32, | |
32 | + lineHeight: '32px', | |
33 | + }} | |
34 | + ><Button type="primary" onClick={handleCancel}>返回</Button> | |
35 | + </div> | |
36 | + </Modal> | |
37 | + ); | |
38 | +} | |
0 | 39 | \ No newline at end of file | ... | ... |
src/pages/crm_new/CluesConnectTargetEffectively/index.tsx
0 → 100644
1 | +import React, {useState} from 'react'; | |
2 | +import { Card, Button, Row, Col } from 'antd'; | |
3 | +import { PageHeaderWrapper } from '@ant-design/pro-layout'; | |
4 | +import { createStore } from '@/hooks/moz'; | |
5 | +import store from './store'; | |
6 | +import List from './components/List'; | |
7 | +import EditModal from './components/EditModal'; | |
8 | +import ShopModal from './components/ShopModal'; | |
9 | +import ShopSelectNew from '@/components/ShopSelectNew'; | |
10 | + | |
11 | +export const { Provider, useStore } = createStore(store); | |
12 | + | |
13 | +function Index() { | |
14 | + const { setParams, setCurrent } = useStore(); | |
15 | + const [selected, setSelected] = useState<any>([]); | |
16 | + | |
17 | + function handleOnChange(value: any) { | |
18 | + setParams({keywords: value[0]?.label || undefined}, true); | |
19 | + setSelected(value || []); | |
20 | + } | |
21 | + return ( | |
22 | + <PageHeaderWrapper title={<Row align="middle"><span style={{width: "5px", height: "20px", backgroundColor: "#448EF7", borderRadius: "3px", display: 'inline-block', marginRight: "10px"}} /><span>线索有效接通目标配置</span></Row>}> | |
23 | + <Card> | |
24 | + <Row justify="space-between" style={{ marginBottom: 20 }}> | |
25 | + <Col span={10}> | |
26 | + <ShopSelectNew value={selected} onChange={handleOnChange} defaultOptions={{bizTypes: "1"}} placeholder="请选择门店" /> | |
27 | + </Col> | |
28 | + <Button onClick={() => setCurrent({visible: true, data: {}})} type="primary">新增</Button> | |
29 | + </Row> | |
30 | + <List /> | |
31 | + <EditModal /> | |
32 | + <ShopModal /> | |
33 | + </Card> | |
34 | + </PageHeaderWrapper> | |
35 | + ); | |
36 | +} | |
37 | + | |
38 | +export default () => <Provider><Index /></Provider>; | ... | ... |
src/pages/crm_new/CluesConnectTargetEffectively/store.ts
0 → 100644
1 | +import React, { useState } from 'react'; | |
2 | +import useInitial from '@/hooks/useInitail'; | |
3 | +import {getConfigApi, Result, ShopList} from './api'; | |
4 | + | |
5 | +interface Current { | |
6 | + visible: boolean | |
7 | + data: Result | |
8 | +} | |
9 | + | |
10 | +interface ShopData { | |
11 | + visible: boolean | |
12 | + data: ShopList[] | |
13 | +} | |
14 | + | |
15 | +export default function useStore() { | |
16 | + const { data, loading, errMsg, setLoading, setParams, params } = useInitial(getConfigApi, [], {}); | |
17 | + const [current, setCurrent] = useState<Current>({visible: false, data: {}}); | |
18 | + const [shopData, setShopData] = useState<ShopData>({visible: false, data: []}); | |
19 | + return { | |
20 | + data, | |
21 | + loading, | |
22 | + errMsg, | |
23 | + setLoading, | |
24 | + setParams, | |
25 | + params, | |
26 | + current, | |
27 | + setCurrent, | |
28 | + shopData, | |
29 | + setShopData | |
30 | + }; | |
31 | +} | |
0 | 32 | \ No newline at end of file | ... | ... |
src/pages/finance/FinanceInvestor/components/ApitalModal.tsx
... | ... | @@ -39,7 +39,7 @@ export default function CreateModal() { |
39 | 39 | }; |
40 | 40 | saveApitalAccountApi(params) |
41 | 41 | .then((res) => { |
42 | - message.success("保存成功"); | |
42 | + message.success(res.result || "保存成功"); | |
43 | 43 | triggerModal(); |
44 | 44 | setSaveLoading(false); |
45 | 45 | setLoading(true); |
... | ... | @@ -53,7 +53,7 @@ export default function CreateModal() { |
53 | 53 | return ( |
54 | 54 | <Modal |
55 | 55 | title={`${visiData.row.id ? "编辑" : "新增"}投资主体`} |
56 | - visible={visiData.visible} | |
56 | + open={visiData.visible} | |
57 | 57 | onOk={form.submit} |
58 | 58 | onCancel={() => triggerModal()} |
59 | 59 | maskClosable={false} |
... | ... | @@ -79,7 +79,7 @@ export default function CreateModal() { |
79 | 79 | </Select> |
80 | 80 | </FormItem> |
81 | 81 | <FormItem name="name" label="投资主体" rules={[{ required: true, message: "请填入投资主体" }]}> |
82 | - <Input placeholder="请填入财务主体" /> | |
82 | + <Input placeholder="请填入投资主体" /> | |
83 | 83 | </FormItem> |
84 | 84 | <FormItem name="includeDealers" label="商家" rules={[{ required: true, message: "请选择商家" }]}> |
85 | 85 | <Select | ... | ... |
src/pages/finance/SpecialAccount/FinancingCompany/components/CreateModal.tsx
... | ... | @@ -10,7 +10,8 @@ const FormItem = Form.Item; |
10 | 10 | const { Option } = Select; |
11 | 11 | |
12 | 12 | export default function CreateModal() { |
13 | - const { visible, current, setCurrent, setVisible, fcList, accountList, setLoading, dealerId, brandList } = useStore(); | |
13 | + const { visible, current, setCurrent, setVisible, fcList, accountList, setLoading, dealerId, brandList, type } = | |
14 | + useStore(); | |
14 | 15 | const [form] = Form.useForm(); |
15 | 16 | |
16 | 17 | const [saveLoading, setSaveLoading] = useState<boolean>(false); |
... | ... | @@ -76,6 +77,10 @@ export default function CreateModal() { |
76 | 77 | } |
77 | 78 | |
78 | 79 | function submit(fieldValue: any) { |
80 | + if (fieldValue.depositAccount.value === fieldValue.account.value) { | |
81 | + message.error("还款保证金账户、一般账户请勿配置为同一账户", 1.5); | |
82 | + return; | |
83 | + } | |
79 | 84 | const param = { |
80 | 85 | dealerId, |
81 | 86 | ...fieldValue, |
... | ... | @@ -155,7 +160,7 @@ export default function CreateModal() { |
155 | 160 | {({ getFieldValue }): any => { |
156 | 161 | const category = getFieldValue("compCategory"); |
157 | 162 | return category ? ( |
158 | - <FormItem label="融资公司" name="fcComp" rules={[{ required: true, message: "请选择融资公司" }]}> | |
163 | + <FormItem label="往来银行" name="fcComp" rules={[{ required: true, message: "请选择往来银行" }]}> | |
159 | 164 | <Select |
160 | 165 | showSearch |
161 | 166 | labelInValue |
... | ... | @@ -185,7 +190,7 @@ export default function CreateModal() { |
185 | 190 | <Radio value={2}>每张票支付</Radio> |
186 | 191 | </Radio.Group> |
187 | 192 | </FormItem> |
188 | - <FormItem name="depositAccount" label="保证金账户"> | |
193 | + <FormItem name="depositAccount" label="还款保证金账户"> | |
189 | 194 | <Select labelInValue placeholder="请选择账户" showSearch optionFilterProp="children" allowClear> |
190 | 195 | {accountList.map((item) => ( |
191 | 196 | <Option key={item.id} value={item.id}> | ... | ... |
src/pages/finance/SpecialAccount/FinancingCompany/components/Filter.tsx
... | ... | @@ -26,7 +26,6 @@ export default function AccountList() { |
26 | 26 | }} |
27 | 27 | > |
28 | 28 | <Row style={{ display: "flex", flex: 1, alignItems: 'center' }}> |
29 | - <div>商家:</div> | |
30 | 29 | <Col span={6}> |
31 | 30 | <Select |
32 | 31 | placeholder="请选择商家" |
... | ... | @@ -44,7 +43,6 @@ export default function AccountList() { |
44 | 43 | ))} |
45 | 44 | </Select> |
46 | 45 | </Col> |
47 | - <div style={{ marginLeft: 20 }}>品牌:</div> | |
48 | 46 | <Col span={6}> |
49 | 47 | <Select |
50 | 48 | placeholder="请选择品牌" | ... | ... |
src/pages/finance/SpecialAccount/FinancingCompany/components/List.tsx
src/pages/mkt/ActivityCreate/ExternalPromotion/components/ActivityFlow/SignUp.tsx
... | ... | @@ -56,6 +56,8 @@ export default function index() { |
56 | 56 | // pa.awardConfigs. |
57 | 57 | } |
58 | 58 | changePa.joinLimitNum = pa.joinLimitNum; |
59 | + changePa.signUpPrice = pa.signUpPrice; | |
60 | + changePa.saleOrderAmount = pa.saleOrderAmount; | |
59 | 61 | changePa.awardConfigs = pa.awardConfigs; |
60 | 62 | changePa.activityNo = activityNo; |
61 | 63 | } |
... | ... | @@ -141,7 +143,7 @@ export default function index() { |
141 | 143 | precision={2} |
142 | 144 | addonAfter="元" |
143 | 145 | style={{ width: "100%" }} |
144 | - disabled={readOnly} | |
146 | + disabled={readOnly && !changeEnable} | |
145 | 147 | /> |
146 | 148 | </Form.Item> |
147 | 149 | {bizType === 2 && ( |
... | ... | @@ -161,7 +163,7 @@ export default function index() { |
161 | 163 | precision={2} |
162 | 164 | addonAfter="元" |
163 | 165 | style={{ width: "100%" }} |
164 | - disabled={readOnly} | |
166 | + disabled={readOnly && !changeEnable} | |
165 | 167 | /> |
166 | 168 | </Form.Item> |
167 | 169 | )} | ... | ... |
src/pages/oop/Car/components/CarList.tsx
src/pages/oop/Car/components/CarModal.tsx
... | ... | @@ -431,6 +431,7 @@ export default function CarModal() { |
431 | 431 | ))} |
432 | 432 | </Select> |
433 | 433 | </FormItem> |
434 | + | |
434 | 435 | <FormItem |
435 | 436 | style={{ width: 400 }} |
436 | 437 | name="specCode" |
... | ... | @@ -501,6 +502,14 @@ export default function CarModal() { |
501 | 502 | <Input.TextArea autoSize allowClear /> |
502 | 503 | </FormItem> |
503 | 504 | <FormItem |
505 | + name="classifyName" | |
506 | + label="车型分类名称" | |
507 | + style={{ width: 400 }} | |
508 | + rules={[{ required: true, message: "该选项为必填项" }]} | |
509 | + > | |
510 | + <Input placeholder="请输入" allowClear /> | |
511 | + </FormItem> | |
512 | + <FormItem | |
504 | 513 | style={{ width: 400 }} |
505 | 514 | name="energyType" |
506 | 515 | label="能源类型" | ... | ... |
src/pages/order3/AddValueTaskConfig/api.ts
src/pages/order3/AddValueTaskConfig/components/EditModal.tsx
1 | 1 | import React, { useState, useEffect } from 'react'; |
2 | -import { Modal, Button, message, Form, InputNumber, DatePicker, Radio } from 'antd'; | |
2 | +import { Modal, Button, message, Form, InputNumber, DatePicker, Radio, Row, Col } from 'antd'; | |
3 | 3 | import {useStore} from '../index'; |
4 | 4 | import {ListResult, saveConfigApi} from '../api'; |
5 | 5 | import { debounce, isNil } from 'lodash'; |
... | ... | @@ -57,7 +57,9 @@ export default function DetailModal() { |
57 | 57 | decoOutputValueRatio: currentData.data?.decoOutputValueRatio, |
58 | 58 | shopList: currentData.data?.shopList?.map((v: any) => ({value: v.shopId, label: v.shopName})) || [], |
59 | 59 | rangeDate: [moment(currentData.data.beginTime), moment(currentData.data.endTime)], |
60 | - addedValueType: currentData?.data.addedValueType | |
60 | + addedValueType: currentData?.data?.addedValueType, | |
61 | + vciOutputValueTask: currentData?.data?.vciOutputValueTask, | |
62 | + decoOutputValueTask: currentData?.data?.decoOutputValueTask | |
61 | 63 | }); |
62 | 64 | setType(currentData.data?.addedValueType || 2); |
63 | 65 | }; |
... | ... | @@ -75,7 +77,9 @@ export default function DetailModal() { |
75 | 77 | shopList: params.shopList.map((v: any) => ({shopId: v.value, shopName: v.label})), |
76 | 78 | beginTime: moment(params.rangeDate[0]).valueOf(), |
77 | 79 | endTime: moment(params.rangeDate[1]).valueOf(), |
78 | - addedValueType: params.addedValueType | |
80 | + addedValueType: params.addedValueType, | |
81 | + vciOutputValueTask: params.vciOutputValueTask, | |
82 | + decoOutputValueTask: params.decoOutputValueTask | |
79 | 83 | }; |
80 | 84 | saveConfigApi(_params) |
81 | 85 | .then(res => { |
... | ... | @@ -129,6 +133,54 @@ export default function DetailModal() { |
129 | 133 | parser={(value: string) => value?.replace('元/台', '')} |
130 | 134 | /> |
131 | 135 | </Form.Item> |
136 | + <Row style={{marginLeft: 30}}> | |
137 | + | |
138 | + <Form.Item | |
139 | + label={<span>其中:<span>商业险产值目标</span></span>} | |
140 | + name="vciOutputValueTask" | |
141 | + style={{width: '100%'}} | |
142 | + rules={[({ getFieldValue }) => ({ | |
143 | + validator(_, value) { | |
144 | + if (isNil(value)) { | |
145 | + return Promise.reject(new Error('请输入')); | |
146 | + } | |
147 | + return Promise.resolve(); | |
148 | + }, | |
149 | + })]} | |
150 | + > | |
151 | + <InputNumber | |
152 | + style={{width: '100%'}} | |
153 | + controls={false} | |
154 | + min={0} | |
155 | + formatter={(value: any) => `${value}元/台`} | |
156 | + precision={2} | |
157 | + parser={(value: string) => value?.replace('元/台', '')} | |
158 | + /> | |
159 | + </Form.Item> | |
160 | + <Form.Item | |
161 | + label={<span style={{textIndent: '42px'}}>装潢产值目标</span>} | |
162 | + name="decoOutputValueTask" | |
163 | + style={{width: '100%'}} | |
164 | + rules={[({ getFieldValue }) => ({ | |
165 | + validator(_, value) { | |
166 | + if (isNil(value)) { | |
167 | + return Promise.reject(new Error('请输入')); | |
168 | + } | |
169 | + return Promise.resolve(); | |
170 | + }, | |
171 | + })]} | |
172 | + > | |
173 | + <InputNumber | |
174 | + style={{width: '100%'}} | |
175 | + controls={false} | |
176 | + min={0} | |
177 | + formatter={(value: any) => `${value}元/台`} | |
178 | + precision={2} | |
179 | + parser={(value: string) => value?.replace('元/台', '')} | |
180 | + /> | |
181 | + </Form.Item> | |
182 | + </Row> | |
183 | + | |
132 | 184 | <div className="addValue_wrapper"><span className="addvalue_text">*</span><span>产值构成:</span></div> |
133 | 185 | <Form.Item |
134 | 186 | label={type === 2 ? "1.计算保险毛利" : "1.计算保险产值:"} | ... | ... |
src/pages/order3/AddValueTaskConfig/components/List.tsx
... | ... | @@ -43,13 +43,19 @@ export default function LargeList({addedValueType} : Props) { |
43 | 43 | rowKey="id" |
44 | 44 | pagination={paginationConfig} |
45 | 45 | > |
46 | - <Column | |
46 | + <Column | |
47 | 47 | title="单车附加值目标" |
48 | 48 | dataIndex="addedValueTask" |
49 | 49 | align="left" |
50 | 50 | render={(_text) => (isNil(_text) ? '-' : <span style={{color: "#FF921C", fontSize: '14px'}}>{_text}元<span style={{color: "#333"}}>/台</span></span>)} |
51 | 51 | /> |
52 | 52 | <Column |
53 | + title="商业险产值目标" | |
54 | + dataIndex="vciOutputValueTask" | |
55 | + align="left" | |
56 | + render={(_text) => (isNil(_text) ? '-' : <div style={{display: 'flex', flexDirection: 'column'}}><span style={{color: "#FF921C", fontSize: '14px'}}>{_text}元<span style={{color: "#333"}}>/台</span></span><span>(含驾意险)</span></div>)} | |
57 | + /> | |
58 | + <Column | |
53 | 59 | title={addedValueType === 2 ? "计算保险毛利" : "计算保险产值"} |
54 | 60 | dataIndex="addedValueTask" |
55 | 61 | align="left" |
... | ... | @@ -61,6 +67,12 @@ export default function LargeList({addedValueType} : Props) { |
61 | 67 | </div> |
62 | 68 | )} |
63 | 69 | /> |
70 | + <Column | |
71 | + title="装潢产值目标" | |
72 | + dataIndex="decoOutputValueTask" | |
73 | + align="left" | |
74 | + render={(_text) => (isNil(_text) ? '-' : <span style={{color: "#FF921C", fontSize: '14px'}}>{_text}元<span style={{color: "#333"}}>/台</span></span>)} | |
75 | + /> | |
64 | 76 | <Column |
65 | 77 | title={addedValueType === 2 ? "计算装潢毛利" : "计算装潢产值"} |
66 | 78 | dataIndex="decoOutputValueRatio" | ... | ... |
src/pages/performance/EvaDataImport/api.ts
... | ... | @@ -52,3 +52,17 @@ export function evaDataIndApi(): http.PromiseResp<any> { |
52 | 52 | export function saveEvaImportData(params: { key: string }) { |
53 | 53 | return request.get(`${MORAX_HOST}/erp/eval-indicator/save-import`, { params }); |
54 | 54 | } |
55 | + | |
56 | +/** 下载人员模板 | |
57 | + * http://testgate.feewee.cn/morax/erp/eval-indicator/import-detail | |
58 | + */ | |
59 | +export function upLoadStaffApi(): http.PromiseResp<any> { | |
60 | + return request.get(`${MORAX_HOST}/erp/eval-indicator/staff/template-file`, {}); | |
61 | +} | |
62 | + | |
63 | +/** 下载门店模板 | |
64 | + * http://testgate.feewee.cn/morax/erp/eval-indicator/import-detail | |
65 | + */ | |
66 | +export function upLoadShopApi(): http.PromiseResp<any> { | |
67 | + return request.get(`${MORAX_HOST}/erp/eval-indicator/shop/template-file`, {}); | |
68 | +} | ... | ... |
src/pages/performance/EvaDataImport/index.tsx
... | ... | @@ -2,7 +2,8 @@ import React, { useState } from "react"; |
2 | 2 | import { PageHeaderWrapper } from "@ant-design/pro-layout"; |
3 | 3 | import { Button, Card, Table, Row, message, Space, Typography, Divider, Switch, Upload } from "antd"; |
4 | 4 | import usePagination from "@/hooks/usePagination"; |
5 | -import { evaDataListApi } from "./api"; | |
5 | +import useInitial from "@/hooks/useInitail"; | |
6 | +import { evaDataListApi, upLoadStaffApi, upLoadShopApi } from "./api"; | |
6 | 7 | import { UploadOutlined } from "@ant-design/icons"; |
7 | 8 | import type { UploadProps } from "antd"; |
8 | 9 | import { history } from "umi"; |
... | ... | @@ -16,8 +17,12 @@ export default () => { |
16 | 17 | const { loading, list, paginationConfig, setParams, innerParams } = usePagination(evaDataListApi, { |
17 | 18 | pageSize: 10, |
18 | 19 | }); |
20 | + const { data: upLoadStaff, loading: upLoadStaffLoading } = useInitial(upLoadStaffApi, [], {}); | |
21 | + const { data: upLoadShop, loading: upLoadShopLoading } = useInitial(upLoadShopApi, [], {}); | |
19 | 22 | const [fileData, setFileData] = useState({}); |
20 | 23 | const [visible, setVisible] = useState<boolean>(false); |
24 | + console.log("upLoadStaff", upLoadStaff); | |
25 | + console.log("upLoadShop", upLoadShop); | |
21 | 26 | const uploadPerson: UploadProps = { |
22 | 27 | name: "file", |
23 | 28 | action: "/api/morax/erp/eval-indicator/analysis-staff", |
... | ... | @@ -59,21 +64,13 @@ export default () => { |
59 | 64 | <Row style={{ marginBottom: 10 }} justify="space-between"> |
60 | 65 | <Filter setParams={setParams} /> |
61 | 66 | <div> |
62 | - <Button type="default" style={{ marginRight: 10 }}> | |
63 | - <a | |
64 | - href="https://gate.feewee.cn/file/download?fid=74d1d724f9be48baa5921f3782037c3b" | |
65 | - target="_blank" | |
66 | - rel="noreferrer" | |
67 | - > | |
67 | + <Button type="default" style={{ marginRight: 10 }} loading={upLoadStaffLoading}> | |
68 | + <a href={upLoadStaff} target="_blank" rel="noreferrer"> | |
68 | 69 | 下载人员模板 |
69 | 70 | </a> |
70 | 71 | </Button> |
71 | - <Button type="default" style={{ marginRight: 10 }}> | |
72 | - <a | |
73 | - href="https://gate.feewee.cn/file/download?fid=e4eb136962164264a78753bd1d6061f3" | |
74 | - target="_blank" | |
75 | - rel="noreferrer" | |
76 | - > | |
72 | + <Button type="default" style={{ marginRight: 10 }} loading={upLoadShopLoading}> | |
73 | + <a href={upLoadShop} target="_blank" rel="noreferrer"> | |
77 | 74 | 下载门店模板 |
78 | 75 | </a> |
79 | 76 | </Button> |
... | ... | @@ -87,12 +84,7 @@ export default () => { |
87 | 84 | </Upload> |
88 | 85 | </div> |
89 | 86 | </Row> |
90 | - <Table | |
91 | - loading={loading} | |
92 | - rowKey={(row) => `id${row.id}`} | |
93 | - dataSource={list} | |
94 | - pagination={paginationConfig} | |
95 | - > | |
87 | + <Table loading={loading} rowKey={(row) => `id${row.id}`} dataSource={list} pagination={paginationConfig}> | |
96 | 88 | <Column |
97 | 89 | title="导入时间" |
98 | 90 | dataIndex="createTime" | ... | ... |
src/pages/performance/EvaGroupSetting/EditComfirm/components/AddRewardsModal.tsx
... | ... | @@ -219,7 +219,6 @@ export default function CreateModal(props: Props) { |
219 | 219 | onChange={(value) => { |
220 | 220 | const _type = value; |
221 | 221 | setCalType(_type); |
222 | - console.log(calType); | |
223 | 222 | }} |
224 | 223 | > |
225 | 224 | <Option value={1} key={1}> |
... | ... | @@ -343,27 +342,29 @@ export default function CreateModal(props: Props) { |
343 | 342 | > |
344 | 343 | {({ getFieldValue }) => { |
345 | 344 | const rankType = getFieldValue("rankType"); |
346 | - if (rankType == 1 && !currentItem.ladders) { | |
345 | + if (rankType == 1 && (!currentItem.ladders || currentItem.ladders.length == 0)) { | |
347 | 346 | form.setFieldValue("ladders", [{ lower: 1, money: 0, key: 0, rankOrderType: 1 }]); |
348 | - } else if (rankType == 2 && !currentItem.ladders) { | |
347 | + } else if (rankType == 2 && (!currentItem.ladders || currentItem.ladders.length == 0)) { | |
348 | + form.setFieldValue("ladders", [{ lower: 0, money: 0, key: 0, rankOrderType: 1 }]); | |
349 | + } else if (!rankType && (!currentItem.ladders || currentItem.ladders.length == 0)) { | |
349 | 350 | form.setFieldValue("ladders", [{ lower: 0, money: 0, key: 0, rankOrderType: 1 }]); |
350 | 351 | } |
351 | - return ( | |
352 | - <Form.Item | |
353 | - name="ladders" | |
354 | - label="排名设置" | |
355 | - rules={[{ required: true, message: "请配置排名" }]} | |
356 | - style={{ width: "100%" }} | |
357 | - > | |
358 | - <RankModal | |
359 | - visible | |
360 | - isPercent={isPercent} | |
361 | - laddersType={currentItem?.laddersType} | |
362 | - calType={calType} | |
363 | - rankType={rankType} | |
364 | - /> | |
365 | - </Form.Item> | |
366 | - ); | |
352 | + return ( | |
353 | + <Form.Item | |
354 | + name="ladders" | |
355 | + label="排名设置" | |
356 | + rules={[{ required: true, message: "请配置排名" }]} | |
357 | + style={{ width: "100%" }} | |
358 | + > | |
359 | + <RankModal | |
360 | + visible | |
361 | + isPercent={isPercent} | |
362 | + laddersType={currentItem?.laddersType} | |
363 | + calType={calType} | |
364 | + rankType={rankType} | |
365 | + /> | |
366 | + </Form.Item> | |
367 | + ); | |
367 | 368 | }} |
368 | 369 | </Form.Item> |
369 | 370 | </> |
... | ... | @@ -433,11 +434,12 @@ export default function CreateModal(props: Props) { |
433 | 434 | > |
434 | 435 | {({ getFieldValue }) => { |
435 | 436 | const caculateType = getFieldValue("calMethod"); |
437 | + // console.log(caculateType, currentItem); | |
436 | 438 | //阶梯(总) |
437 | 439 | if (caculateType == 3 || caculateType == 5) { |
438 | - if (caculateType == 3 && !currentItem.ladders) { | |
440 | + if (caculateType == 3 && (!currentItem.ladders || currentItem.ladders.length == 0)) { | |
439 | 441 | form.setFieldValue("ladders", [{ lower: 0, money: 0, key: 0 }]); |
440 | - } else if (caculateType == 5 && !currentItem.ladders) { | |
442 | + } else if (caculateType == 5 && (!currentItem.ladders || currentItem.ladders.length == 0)) { | |
441 | 443 | form.setFieldValue("ladders", [{ lower: 0, money: 0, capMoney: 0, key: 0 }]); |
442 | 444 | } |
443 | 445 | return ( | ... | ... |
src/pages/performance/EvaGroupSetting/EditComfirm/components/CondLaddersTable.tsx
... | ... | @@ -205,10 +205,11 @@ const LadderTable = ({ value, onChange, readOnly, visible, type, setladderVisibl |
205 | 205 | render: (value: number) => (value && value !== 65536 ? value + "%" : ""), |
206 | 206 | }, |
207 | 207 | { |
208 | - title: "标准分", | |
208 | + title: "绩效分折算比例", | |
209 | 209 | dataIndex: "scorePercent", |
210 | 210 | width: "15%", |
211 | 211 | editable: true, |
212 | + render: (value: number) => value + "%", | |
212 | 213 | }, |
213 | 214 | { |
214 | 215 | title: "操作", | ... | ... |
src/pages/performance/EvaGroupSetting/EditComfirm/components/RankModal.tsx
... | ... | @@ -166,6 +166,7 @@ const TotalAmount = ({ |
166 | 166 | }; |
167 | 167 | |
168 | 168 | const _add = async (key: React.Key, record: Item) => { |
169 | + // console.log("record.key", record.key); | |
169 | 170 | try { |
170 | 171 | const row = (await form.validateFields()) as Item; |
171 | 172 | let newData = [...value.map((i) => ({ ...i }))]; |
... | ... | @@ -320,7 +321,11 @@ const TotalAmount = ({ |
320 | 321 | const editable = isEditing(record); |
321 | 322 | return editable ? ( |
322 | 323 | <Space split={<Divider type="vertical" />}> |
323 | - <Typography.Link onClick={() => _add(record.key, record)} style={{ marginRight: 8 }}> | |
324 | + <Typography.Link | |
325 | + onClick={() => _add(record.key, record)} | |
326 | + style={{ marginRight: 8 }} | |
327 | + disabled={index !== value?.length - 1} | |
328 | + > | |
324 | 329 | 保存并新增排名区间 |
325 | 330 | </Typography.Link> |
326 | 331 | <Typography.Link onClick={() => save(record.key, record)} style={{ marginRight: 8 }}> | ... | ... |
src/pages/performance/KpiGroupSetting/EditComfirm/components/CondLaddersTable.tsx
... | ... | @@ -202,13 +202,14 @@ const LadderTable = ({ value, onChange, readOnly, visible, type, setladderVisibl |
202 | 202 | dataIndex: "upper", |
203 | 203 | width: "20%", |
204 | 204 | editable: true, |
205 | - render: (value: number) => ((value && value !== 65536) ? (value + "%") : ""), | |
205 | + render: (value: number) => (value && value !== 65536 ? value + "%" : ""), | |
206 | 206 | }, |
207 | 207 | { |
208 | - title: "标准分", | |
208 | + title: "绩效分折算比例", | |
209 | 209 | dataIndex: "scorePercent", |
210 | 210 | width: "15%", |
211 | 211 | editable: true, |
212 | + render: (value: number) => value + "%", | |
212 | 213 | }, |
213 | 214 | { |
214 | 215 | title: "操作", | ... | ... |
src/pages/pms/comonents/PmsSelect.tsx
... | ... | @@ -7,8 +7,7 @@ export default function Index(props: SelectProps) { |
7 | 7 | <Select |
8 | 8 | {...props} |
9 | 9 | showSearch |
10 | - // optionFilterProp="children" | |
11 | - filterOption={(input: any, option: any) => option.children.toLowerCase().indexOf(input.toLowerCase()) >= 0} | |
10 | + optionFilterProp="label" | |
12 | 11 | /> |
13 | 12 | ); |
14 | 13 | } |
15 | 14 | \ No newline at end of file | ... | ... |
src/pages/pms/partPlan/CustBuyPlan/subpages/First/components/PartModal.tsx
... | ... | @@ -73,7 +73,7 @@ export default function DetailModal() { |
73 | 73 | align="center" |
74 | 74 | render={r => ( |
75 | 75 | <> |
76 | - <Popconfirm | |
76 | + {/* <Popconfirm | |
77 | 77 | title="确定删除?" |
78 | 78 | onConfirm={() => deletePart(r)} |
79 | 79 | okText="确定" |
... | ... | @@ -81,7 +81,7 @@ export default function DetailModal() { |
81 | 81 | > |
82 | 82 | <a>删除</a> |
83 | 83 | </Popconfirm> |
84 | - <Divider type="vertical" /> | |
84 | + <Divider type="vertical" /> */} | |
85 | 85 | <a onClick={() => onFinish(r)}>完成</a> |
86 | 86 | </> |
87 | 87 | )} | ... | ... |
src/pages/pms/partPlan/PlanManage/api.ts
... | ... | @@ -20,13 +20,13 @@ export interface Params { |
20 | 20 | * 列表项 |
21 | 21 | */ |
22 | 22 | export interface ListVO { |
23 | - planId?: string; // 计划Id | |
23 | + planId?: number; // 计划Id | |
24 | 24 | planNo?: string; // 计划单号 |
25 | 25 | brandId?: string; // 品牌ID |
26 | 26 | planDate?: string; // 计划日期 |
27 | 27 | approvalOrderNo?: string; // 审核单号 |
28 | 28 | userName?: string; // 计划人员 |
29 | - status?: string; // 计划状态 1:待审核2:已通过9:已拒绝 | |
29 | + status?: number; // 计划状态 1:待审核2:已通过9:已拒绝 | |
30 | 30 | settleShopId?: string; // 结算门店门店id |
31 | 31 | settleShopName?: string; // 结算门店名称 |
32 | 32 | prepayToken?: string; // 预付token | ... | ... |
src/pages/pms/partPlan/PlanManage/components/DetailModal.tsx
... | ... | @@ -6,7 +6,7 @@ import ProgressView from "./ProgressView"; |
6 | 6 | import ShippingDetailModal from './ShippingDetailModal'; |
7 | 7 | import PartDetailModal from './PartDetailModal'; |
8 | 8 | import {flattenDeep} from "@/pages/pms/entity"; |
9 | -import {getDetail, DetailVO} from "@/pages/pms/partPlan/PlanManage/subpages/Detail/api"; | |
9 | +import {getDetail, DetailVO, Item} from "@/pages/pms/partPlan/PlanManage/subpages/Detail/api"; | |
10 | 10 | |
11 | 11 | interface Props { |
12 | 12 | visible: boolean, |
... | ... | @@ -22,12 +22,12 @@ export default function DetailModal(props: Props) { |
22 | 22 | // @ts-ignore |
23 | 23 | const { data: progresss, setParams: setProgresssParams } = useInitial<ProgressVO[], Params>(getProgressDetail, [], params, delay); |
24 | 24 | // @ts-ignore |
25 | - const { data, setParams, loading } = useInitial<DetailVO[], Params>(getDetail, [], params, delay); | |
25 | + const { data, setParams, loading } = useInitial<Item, Params>(getDetail, {}, params, delay); | |
26 | 26 | const [visibleDetail, setVisibleDetail] = useState(false); |
27 | 27 | const [_item, setItem] = useState<SubmitListVO>({}); |
28 | 28 | const [visiblePartDetail, setVisiblePartDetail] = useState(false); |
29 | 29 | const suppliers = useMemo(() => { |
30 | - return flattenDeep(data.map((it: DetailVO) => (it.suppliers || []).map(su => ({...it, ...su})))); | |
30 | + return flattenDeep(data.list?.map((it: DetailVO) => (it.suppliers || []).map(su => ({...it, ...su})))); | |
31 | 31 | }, [data]); |
32 | 32 | |
33 | 33 | useEffect(() => { | ... | ... |
src/pages/pms/partPlan/PlanManage/index.tsx
... | ... | @@ -13,6 +13,7 @@ import DetailModal from "@/pages/pms/partPlan/PlanManage/components/DetailModal" |
13 | 13 | |
14 | 14 | // 计划状态 1:待审核2:已通过3待付款4已付款9:已拒绝 |
15 | 15 | const statusName: {[key: number]: string} = { |
16 | + 0: '草稿', | |
16 | 17 | 1: '待审核', |
17 | 18 | 2: '已通过', |
18 | 19 | 3: '待付款', |
... | ... | @@ -69,6 +70,7 @@ export default function Index() { |
69 | 70 | onChange={v => setParams({status: v }, true)} |
70 | 71 | optionFilterProp="children" |
71 | 72 | > |
73 | + <Select.Option value={0} key={0}>草稿</Select.Option> | |
72 | 74 | <Select.Option value={1} key={1}>待审核</Select.Option> |
73 | 75 | <Select.Option value={2} key={2}>已通过</Select.Option> |
74 | 76 | <Select.Option value={9} key={9}>已拒绝</Select.Option> |
... | ... | @@ -95,9 +97,16 @@ export default function Index() { |
95 | 97 | fixed="right" |
96 | 98 | render={(text, _item: ListVO) => ( |
97 | 99 | <> |
98 | - <a onClick={() => history.push(`/pms/partPlan/planManage/detail/${_item.planId}`)}> | |
99 | - 查看 | |
100 | - </a> | |
100 | + {[0, 9].includes(_item.status || 0) ? ( | |
101 | + <a onClick={() => history.push(`/pms/partPlan/planManage/apply?planId=${_item.planId}`)}> | |
102 | + 编辑 | |
103 | + </a> | |
104 | + ) | |
105 | + : ( | |
106 | + <a onClick={() => history.push(`/pms/partPlan/planManage/detail/${_item.planId}`)}> | |
107 | + 查看 | |
108 | + </a> | |
109 | + )} | |
101 | 110 | <Divider type="vertical" /> |
102 | 111 | <a onClick={() => { |
103 | 112 | setItem(_item); | ... | ... |
src/pages/pms/partPlan/PlanManage/subpages/Apply/api.ts
... | ... | @@ -16,3 +16,9 @@ export interface SaveParams { |
16 | 16 | export function saveApi(params?: SaveParams): http.PromiseResp<void> { |
17 | 17 | return request.post(`${PMS_HOST}/erp/plan/pool/save/plan`, params); |
18 | 18 | } |
19 | +/** | |
20 | + * 草稿 | |
21 | + */ | |
22 | +export function draftApi(params?: SaveParams): http.PromiseResp<void> { | |
23 | + return request.post(`${PMS_HOST}/erp/plan/pool/save/draft`, params); | |
24 | +} | ... | ... |
src/pages/pms/partPlan/PlanManage/subpages/Apply/components/DealerModal.tsx
... | ... | @@ -11,9 +11,9 @@ interface Props { |
11 | 11 | } |
12 | 12 | const {Option} = Select; |
13 | 13 | export default function Index({ onCancel, visible, onOk, dealerList = [] }: Props) { |
14 | - const [dealer, setDealer] = useState<any>({dealerId: null, dealerName: null}); | |
14 | + const [dealer, setDealer] = useState<any>({ settleDealerId: null, settleDealerName: null}); | |
15 | 15 | const { data: dealers } = useInitail<CommonApi.OptionVO[], CommonApi.DealerParam>(getDealerApi, [], {}); |
16 | - const suIds = dealerList.map(it => it.dealerId); | |
16 | + const suIds = dealerList.map(it => it.settleDealerId); | |
17 | 17 | |
18 | 18 | useEffect(() => { |
19 | 19 | if (!visible) { |
... | ... | @@ -22,7 +22,7 @@ export default function Index({ onCancel, visible, onOk, dealerList = [] }: Prop |
22 | 22 | }, [visible]); |
23 | 23 | |
24 | 24 | const handSave = () => { |
25 | - if (!dealer.dealerId) { | |
25 | + if (!dealer.settleDealerId) { | |
26 | 26 | message.error('请选择采购商家'); |
27 | 27 | return; |
28 | 28 | } |
... | ... | @@ -40,7 +40,7 @@ export default function Index({ onCancel, visible, onOk, dealerList = [] }: Prop |
40 | 40 | <Button key="cancel" onClick={onCancel}>取消</Button>, |
41 | 41 | <Button |
42 | 42 | key="submit" |
43 | - disabled={!dealer.dealerId} | |
43 | + disabled={!dealer.settleDealerId} | |
44 | 44 | onClick={handSave} |
45 | 45 | type="primary" |
46 | 46 | htmlType="submit" |
... | ... | @@ -52,14 +52,14 @@ export default function Index({ onCancel, visible, onOk, dealerList = [] }: Prop |
52 | 52 | <div style={{display: 'flex', flexDirection: 'row', alignItems: 'center', marginBottom: 10}}> |
53 | 53 | <span>商家:</span> |
54 | 54 | <Select |
55 | - value={dealer.dealerId} | |
55 | + value={dealer.settleDealerId} | |
56 | 56 | style={{ width: 250 }} |
57 | 57 | placeholder="请选择商家" |
58 | 58 | showSearch |
59 | 59 | optionFilterProp="children" |
60 | - onChange={(dealerId) => { | |
61 | - const d = dealers.find(it => it.id == dealerId) || {}; | |
62 | - setDealer({dealerId, dealerName: d.name}); | |
60 | + onChange={(settleDealerId) => { | |
61 | + const d = dealers.find(it => it.id == settleDealerId) || {}; | |
62 | + setDealer({ settleDealerId, settleDealerName: d.name}); | |
63 | 63 | }} |
64 | 64 | > |
65 | 65 | {dealers.filter(it => !suIds.includes(it.id)).map((b) => ( | ... | ... |
src/pages/pms/partPlan/PlanManage/subpages/Apply/index.tsx
1 | -import {Card, ConfigProvider, Select, Button, message, Popconfirm} from 'antd'; | |
1 | +import {Card, ConfigProvider, Select, Button, message, Popconfirm, Input} from 'antd'; | |
2 | 2 | import { PageHeaderWrapper } from '@ant-design/pro-layout'; |
3 | 3 | import React, {useEffect, useState} from "react"; |
4 | 4 | import DealerModal from './components/DealerModal'; |
... | ... | @@ -8,16 +8,25 @@ import {throttle, sum} from 'lodash'; |
8 | 8 | import useInitial from "@/hooks/useInitail"; |
9 | 9 | import * as API from "@/common/api"; |
10 | 10 | import {getList, Params, ListVO} from "@/pages/pms/partPlan/PlanPool/api"; |
11 | -import {saveApi} from './api'; | |
11 | +import {saveApi, draftApi} from './api'; | |
12 | 12 | import zhCN from "antd/lib/locale-provider/zh_CN"; |
13 | 13 | import st from "@/pages/pms/partPlan/PlanManage/style.less"; |
14 | -import StepBnt from "@/pages/pms/comonents/StepBnt"; | |
15 | 14 | import PartModal from "@/pages/pms/partPlan/PlanManage/subpages/Apply/components/PartModal"; |
16 | 15 | import {groupBys, flattenDeep} from '@/pages/pms/entity'; |
17 | 16 | import {PartDetail} from '../../api'; |
17 | +import { history } from 'umi'; | |
18 | +import { getDetail, Params as detailParams, Item } from '../Detail/api'; | |
19 | +import ImageUpload from '@/pages/decoration/deco/DeoGoodsManagement/components/ImageUpload'; | |
18 | 20 | |
19 | 21 | const { Option } = Select; |
22 | +const apiObj: { [key: number]: any } = { | |
23 | + 1: saveApi, | |
24 | + 2: draftApi | |
25 | +}; | |
20 | 26 | export default function Index() { |
27 | + const planId = history.location.query; | |
28 | + const [detaildelay, setDetaildelay] = useState(true); | |
29 | + const { data, setParams: detailsetParams } = useInitial<Item, detailParams>(getDetail, {}, { ...planId }, detaildelay); | |
21 | 30 | const [delay, setDelay] = useState<boolean>(true); |
22 | 31 | const [loading, setLoading] = useState<boolean>(false); |
23 | 32 | const { data: brands } = useInitial(API.getBrandFilterApi, [], {}); |
... | ... | @@ -31,10 +40,30 @@ export default function Index() { |
31 | 40 | const [visiblePartDetail, setVisiblePartDetail] = useState(false); |
32 | 41 | const partList = flattenDeep(dealerList.map(it => (it.suppliers || []).map((su: any) => (su.storages || []).map((st: any) => (st.parts || []))))); |
33 | 42 | const poolIds = partList.map((it: any) => it.poolId); |
43 | + const [imageVisible, setImageVisible] = useState(true); | |
44 | + const [info, setInfo] = useState<{ remark?: string, fids?: any }>(); | |
45 | + | |
46 | + useEffect(() => { | |
47 | + if (planId?.planId) { | |
48 | + setDetaildelay(false); | |
49 | + detailsetParams({ ...planId }, true); | |
50 | + } | |
51 | + }, []); | |
52 | + | |
53 | + useEffect(() => { | |
54 | + if (data.list?.length) { | |
55 | + setImageVisible(false); | |
56 | + setDfParams({brandId: data.list[0].brandId}); | |
57 | + setParams({}, true); | |
58 | + setDelay(false); | |
59 | + setDealerList(data.list); | |
60 | + setInfo({ remark: data.remark, fids: data.fids?.split(',').map(i => `/api/file/show?fid=${i}`) }); | |
61 | + } | |
62 | + }, [data.list?.length]); | |
34 | 63 | |
35 | 64 | function onOk(parts: ListVO[] = []) { |
36 | 65 | setDealerList(dealerList.map(it => { |
37 | - if (it.dealerId == dealer.dealerId) { | |
66 | + if (it.settleDealerId == dealer.settleDealerId) { | |
38 | 67 | return { |
39 | 68 | ...it, |
40 | 69 | suppliers: (it.suppliers || []).map((su: any) => { |
... | ... | @@ -64,7 +93,7 @@ export default function Index() { |
64 | 93 | |
65 | 94 | function onOkSupplier(supplier: any = {}) { |
66 | 95 | setDealerList(dealerList.map(it => { |
67 | - if (it.dealerId == dealer.dealerId) { | |
96 | + if (it.settleDealerId == dealer.settleDealerId) { | |
68 | 97 | return { |
69 | 98 | ...it, |
70 | 99 | suppliers: [...(it.suppliers || []), supplier] |
... | ... | @@ -76,7 +105,7 @@ export default function Index() { |
76 | 105 | |
77 | 106 | function deleteSupplier(dealer: any = {}, supplier: any = {}) { |
78 | 107 | setDealerList(dealerList.map(it => { |
79 | - if (it.dealerId == dealer.dealerId) { | |
108 | + if (it.settleDealerId == dealer.settleDealerId) { | |
80 | 109 | return { |
81 | 110 | ...it, |
82 | 111 | suppliers: (it.suppliers || []).filter((su: any) => su.supplierId != supplier.supplierId) |
... | ... | @@ -88,7 +117,7 @@ export default function Index() { |
88 | 117 | |
89 | 118 | function deletePart(dealer: any = {}, supplier: any = {}, storage: any = {}, part: any = {}) { |
90 | 119 | setDealerList(dealerList.map(it => { |
91 | - if (it.dealerId == dealer.dealerId) { | |
120 | + if (it.settleDealerId == dealer.settleDealerId) { | |
92 | 121 | return { |
93 | 122 | ...it, |
94 | 123 | suppliers: (it.suppliers || []).map((su: any) => { |
... | ... | @@ -116,7 +145,7 @@ export default function Index() { |
116 | 145 | |
117 | 146 | function deleteStorage(dealer: any = {}, supplier: any = {}, storage: any = {}) { |
118 | 147 | setDealerList(dealerList.map(it => { |
119 | - if (it.dealerId == dealer.dealerId) { | |
148 | + if (it.settleDealerId == dealer.settleDealerId) { | |
120 | 149 | return { |
121 | 150 | ...it, |
122 | 151 | suppliers: (it.suppliers || []).map((su: any) => { |
... | ... | @@ -134,31 +163,34 @@ export default function Index() { |
134 | 163 | })); |
135 | 164 | } |
136 | 165 | |
137 | - // useEffect(() => { | |
138 | - // if (dfParams.brandId) { | |
139 | - // setParams(dfParams, true); | |
140 | - // setDelay(false); | |
141 | - // } | |
142 | - // }, [dfParams.brandId]); | |
143 | - | |
144 | - const onSubmit = throttle(() => { | |
166 | + const onSubmit = throttle((isSave) => { | |
145 | 167 | setLoading(true); |
146 | 168 | let suppliers = flattenDeep(dealerList.map(de => (de.suppliers || []).map((su: any) => ({...de, ...su})))); |
147 | 169 | suppliers = suppliers.map(su => ({ |
148 | 170 | ...su, |
149 | - settleDealerId: su.dealerId, | |
171 | + settleDealerId: su.settleDealerId, | |
150 | 172 | storages: (su.storages || []).map((st: any) => ({ |
151 | 173 | storageId: st.storageId, |
152 | 174 | poolIds: (st.parts || []).map((pa: any) => pa.poolId) |
153 | 175 | })) |
154 | 176 | })); |
155 | - saveApi({ | |
177 | + const type = isSave ? 1 : 2; | |
178 | + apiObj[type]({ | |
179 | + planId: Number(planId?.planId), | |
156 | 180 | ...dfParams, |
157 | - suppliers | |
181 | + suppliers, | |
182 | + remark: info?.remark, | |
183 | + fids: info?.fids.map((i: any) => { | |
184 | + if (typeof i == 'object') { | |
185 | + return i.url.split('=')[1]; | |
186 | + } else { | |
187 | + return i; | |
188 | + } | |
189 | + }).join() | |
158 | 190 | }).then(() => { |
159 | 191 | setLoading(false); |
160 | - history.back(); | |
161 | - }).catch(e => { | |
192 | + history.goBack(); | |
193 | + }).catch((e: any) => { | |
162 | 194 | setLoading(false); |
163 | 195 | message.error(e.message); |
164 | 196 | }); |
... | ... | @@ -204,15 +236,15 @@ export default function Index() { |
204 | 236 | </a> |
205 | 237 | </div> |
206 | 238 | {dealerList.map((dealer: any = {}) => ( |
207 | - <div key={`dealer${dealer.dealerId}`} style={{ marginTop: 10 }}> | |
239 | + <div key={`dealer${dealer.settleDealerId}`} style={{ marginTop: 10 }}> | |
208 | 240 | <div style={{ display: 'flex', flexDirection: 'row', alignItems: 'center'}}> |
209 | - <div style={{ fontWeight: "bold" }}>{`商家: ${dealer.dealerName || ''}`}</div> | |
241 | + <div style={{ fontWeight: "bold" }}>{`商家: ${dealer.settleDealerName || ''}`}</div> | |
210 | 242 | <a style={{marginLeft: 40}} onClick={() => { setVisibleSupplier(true); setDealer(dealer); }}> |
211 | 243 | 添加指定供应商 |
212 | 244 | </a> |
213 | 245 | <Popconfirm |
214 | 246 | title="是否删除" |
215 | - onConfirm={() => setDealerList(dealerList.filter(it => it.dealerId!=dealer.dealerId))} | |
247 | + onConfirm={() => setDealerList(dealerList.filter(it => it.settleDealerId!=dealer.settleDealerId))} | |
216 | 248 | okText="确定" |
217 | 249 | cancelText="取消" |
218 | 250 | style={{marginLeft: 20}} |
... | ... | @@ -224,8 +256,6 @@ export default function Index() { |
224 | 256 | </div> |
225 | 257 | {(dealer.suppliers || []).map((supplier: any = {}) => { |
226 | 258 | const paList: any[] = flattenDeep((supplier.storages || []).map((st: any) => (st.parts || []))); |
227 | - // const paList: any[] = pas.length > 0 ? pas[0] : []; | |
228 | - console.log('pas', paList); | |
229 | 259 | return ( |
230 | 260 | <div key={`supplier${supplier.supplierId}`} style={{ marginTop: 10, marginLeft: 40 }}> |
231 | 261 | <div style={{ display: 'flex', flexDirection: 'row', alignItems: 'center'}}> |
... | ... | @@ -247,7 +277,7 @@ export default function Index() { |
247 | 277 | </div> |
248 | 278 | <div style={{display: 'flex', marginLeft: 40, marginTop: 10}}> |
249 | 279 | <div style={{marginRight: 20}}> |
250 | - {`总金额: ${sum(paList.map((it: any) => (it.price || 0) * (it.count || 0))).toFixed(2)}元`} | |
280 | + {`总金额: ${sum(paList.map((it: any) => (it.price || 0) * (it.count || it.partCnt || 0))).toFixed(2)}元`} | |
251 | 281 | </div> |
252 | 282 | <div>{`品种数: ${[...new Set((paList || []).map((i: PartDetail) => i.partCode))].length}种`}</div> |
253 | 283 | </div> |
... | ... | @@ -255,7 +285,7 @@ export default function Index() { |
255 | 285 | <div key={`storage${storage.storageId}`} style={{ marginTop: 10, marginLeft: 60 }}> |
256 | 286 | <div style={{ display: 'flex', flexDirection: 'row', alignItems: 'center'}}> |
257 | 287 | <div style={{ fontWeight: "bold" }}>{`发运库房: ${storage.storageName || ''}`}</div> |
258 | - <div style={{marginLeft: 20}}>{`总金额: ${sum((storage.parts || []).map((it: any) => (it.price || 0) * (it.count || 0))).toFixed(2)}元`}</div> | |
288 | + <div style={{marginLeft: 20}}>{`总金额: ${sum((storage.parts || []).map((it: any) => (it.price || 0) * (it.count || it.partCnt || 0))).toFixed(2)}元`}</div> | |
259 | 289 | <div style={{marginLeft: 20}}>{`品种数: ${[...new Set((storage.parts || []).map((i: PartDetail) => i.partCode))].length}种`}</div> |
260 | 290 | <a style={{marginLeft: 20}} onClick={() => { setVisiblePartDetail(true); setDealer({dealer, supplier, storage }); }}> |
261 | 291 | 查看配件 |
... | ... | @@ -308,11 +338,49 @@ export default function Index() { |
308 | 338 | deletePart={deletePart} |
309 | 339 | /> |
310 | 340 | </div> |
311 | - <StepBnt | |
312 | - disable={loading} | |
313 | - bntLoading={loading} | |
314 | - submit={onSubmit} | |
315 | - /> | |
341 | + <div style={{display: 'flex', alignItems: 'flex-start', marginBottom: 20}}> | |
342 | + <span style={{marginRight: 10}}>备注:</span> | |
343 | + <Input.TextArea rows={2} style={{ width: 250 }} value={info?.remark} onChange={e => setInfo({...info, remark: e.target.value})} /> | |
344 | + </div> | |
345 | + <div style={{ display: 'flex', alignItems: 'flex-start' }}> | |
346 | + <span style={{ marginRight: 10 }}>附件:</span> | |
347 | + <ImageUpload | |
348 | + length={6} | |
349 | + clear={imageVisible} | |
350 | + value={info?.fids} | |
351 | + onChange={v => { | |
352 | + const ids = v.fileList.map(it => it?.response?.data || it); | |
353 | + setInfo({ ...info, fids: ids }); | |
354 | + }} | |
355 | + /> | |
356 | + </div> | |
357 | + <div style={{display: 'flex', justifyContent: 'center'}}> | |
358 | + <Button | |
359 | + disabled={loading} | |
360 | + loading={loading} | |
361 | + style={{marginRight: 20}} | |
362 | + onClick={() => { setImageVisible(true); history.goBack(); }} | |
363 | + > | |
364 | + 取消 | |
365 | + </Button> | |
366 | + <Button | |
367 | + disabled={loading} | |
368 | + loading={loading} | |
369 | + onClick={() => onSubmit(false)} | |
370 | + type="primary" | |
371 | + style={{marginRight: 20}} | |
372 | + > | |
373 | + 暂存为草稿 | |
374 | + </Button> | |
375 | + <Button | |
376 | + disabled={loading} | |
377 | + loading={loading} | |
378 | + onClick={() => onSubmit(true)} | |
379 | + type="primary" | |
380 | + > | |
381 | + 确认并提交 | |
382 | + </Button> | |
383 | + </div> | |
316 | 384 | </Card> |
317 | 385 | </ConfigProvider> |
318 | 386 | </PageHeaderWrapper> | ... | ... |
src/pages/pms/partPlan/PlanManage/subpages/Detail/api.ts
... | ... | @@ -7,6 +7,7 @@ import { PMS_HOST } from '@/utils/host'; |
7 | 7 | */ |
8 | 8 | export interface DetailVO { |
9 | 9 | brandName?: string; // 品牌名称 |
10 | + brandId?: number; // 品牌ID | |
10 | 11 | settleDealerName?: string; // 结算商家 |
11 | 12 | settleShopName?: string; // 结算门店 |
12 | 13 | suppliers?: SupplierVO[]; // 配件集合 |
... | ... | @@ -47,9 +48,55 @@ export interface Params { |
47 | 48 | planNo?: string; |
48 | 49 | no?: string; |
49 | 50 | } |
51 | +export interface Item { | |
52 | + /** | |
53 | + * 计划单号 | |
54 | + */ | |
55 | + planNo?: string; | |
56 | + /** | |
57 | + * 品牌ID | |
58 | + */ | |
59 | + brandId?: number; | |
60 | + /** | |
61 | + * 计划日期 | |
62 | + */ | |
63 | + planDate?: string; | |
64 | + /** | |
65 | + * 审核单号 | |
66 | + */ | |
67 | + approvalOrderNo?: string; | |
68 | + /** | |
69 | + * 计划人员 | |
70 | + */ | |
71 | + userName?: string; | |
72 | + /** | |
73 | + * 库销比 | |
74 | + */ | |
75 | + ratio?: number; | |
76 | + /** | |
77 | + * 计划库销比 | |
78 | + */ | |
79 | + planRatio?: number; | |
80 | + /** | |
81 | + * 计划状态 1:待审核2:已通过9:已拒绝 | |
82 | + */ | |
83 | + status?: string; | |
84 | + /** | |
85 | + * 备注 | |
86 | + */ | |
87 | + remark?: string; | |
88 | + /** | |
89 | + * 附件 | |
90 | + */ | |
91 | + fids?: string; | |
92 | + /** | |
93 | + * 计划列表 | |
94 | + */ | |
95 | + list?: DetailVO[]; | |
96 | +} | |
50 | 97 | /** |
51 | 98 | * 查询明细 |
52 | 99 | */ |
53 | -export function getDetail(params?: Params): http.PromiseResp<DetailVO[]> { | |
100 | +export function getDetail(params?: Params): http.PromiseResp<Item> { | |
54 | 101 | return request.get(`${PMS_HOST}/erp/part/plan/get/plan/group/detail`, { params }); |
55 | 102 | } | ... | ... |
src/pages/pms/partPlan/PlanManage/subpages/Detail/index.tsx
1 | -import {Card, ConfigProvider, Spin} from 'antd'; | |
1 | +import { Card, ConfigProvider, Spin, Upload } from 'antd'; | |
2 | 2 | import { PageHeaderWrapper } from '@ant-design/pro-layout'; |
3 | 3 | import React, {useState} from "react"; |
4 | 4 | import useInitial from "@/hooks/useInitail"; |
5 | -import {getDetail, DetailVO, Params, SupplierVO, StorageVO, PartVO} from "./api"; | |
5 | +import { getDetail, Item, Params, SupplierVO, StorageVO, PartVO, DetailVO } from "./api"; | |
6 | 6 | import zhCN from "antd/lib/locale-provider/zh_CN"; |
7 | 7 | import st from "@/pages/pms/partPlan/PlanManage/style.less"; |
8 | 8 | import StepBnt from "@/pages/pms/comonents/StepBnt"; |
... | ... | @@ -14,7 +14,7 @@ import PartDetailModal from './components/PartDetailModal'; |
14 | 14 | |
15 | 15 | export default function Index({ match }: common.ConnectProps) { |
16 | 16 | const { planId } = match.params; |
17 | - const { data, loading } = useInitial<DetailVO[], Params>(getDetail, [], {planId}); | |
17 | + const { data, loading } = useInitial<Item, Params>(getDetail, {}, {planId}); | |
18 | 18 | const [visiblePart, setVisiblePart] = useState(false); |
19 | 19 | const [parts, setParts] = useState<PartVO[]>([]); |
20 | 20 | |
... | ... | @@ -23,7 +23,7 @@ export default function Index({ match }: common.ConnectProps) { |
23 | 23 | <Spin spinning={loading}> |
24 | 24 | <ConfigProvider locale={zhCN}> |
25 | 25 | <Card className={st.page}> |
26 | - {data.map((dealer: DetailVO = {}) => ( | |
26 | + {data.list?.map((dealer: DetailVO = {}) => ( | |
27 | 27 | <div key={`dealer${dealer.settleDealerName}`} style={{ marginTop: 10 }}> |
28 | 28 | <div style={{ fontWeight: "bold" }}>{`商家: ${dealer.settleDealerName || ''}`}</div> |
29 | 29 | {(dealer.suppliers || []).map((supplier: SupplierVO = {}) => { |
... | ... | @@ -57,6 +57,26 @@ export default function Index({ match }: common.ConnectProps) { |
57 | 57 | })} |
58 | 58 | </div> |
59 | 59 | ))} |
60 | + <div style={{ display: 'flex', alignItems: 'flex-start', marginBottom: 20 }}> | |
61 | + <span style={{ marginRight: 10 }}>备注:{data.remark}</span> | |
62 | + </div> | |
63 | + <div style={{ display: 'flex', alignItems: 'flex-start' }}> | |
64 | + <span style={{ marginRight: 10 }}>附件:</span> | |
65 | + <div style={{display: 'flex'}}> | |
66 | + {data.fids?.split(',').map(i => ( | |
67 | + <Upload | |
68 | + listType="picture-card" | |
69 | + fileList={[{ | |
70 | + uid: '-1', | |
71 | + name: i, | |
72 | + status: 'done', | |
73 | + url: `/api/file/show?fid=${i}`, | |
74 | + }]} | |
75 | + disabled | |
76 | + /> | |
77 | + ))} | |
78 | + </div> | |
79 | + </div> | |
60 | 80 | <PartDetailModal visible={visiblePart} onCancel={() => setVisiblePart(false)} parts={parts} /> |
61 | 81 | <StepBnt |
62 | 82 | disable={false} | ... | ... |
src/pages/pms/partPlan/PlanPool/api.ts
1 | 1 | import { http } from '@/typing/http'; |
2 | 2 | import request from '@/utils/request'; |
3 | 3 | import { PMS_HOST } from '@/utils/host'; |
4 | +import { PartVO } from '@/pages/pms/partPlan/CustBuyPlan/api'; | |
4 | 5 | |
5 | 6 | export interface Params { |
6 | 7 | brandId?: number, // 品牌ID |
... | ... | @@ -18,6 +19,7 @@ export interface Params { |
18 | 19 | |
19 | 20 | export interface ListVO { |
20 | 21 | poolId: number; // 计划池id |
22 | + poolIds?: string, | |
21 | 23 | id?: number; |
22 | 24 | name?: string; // 名称 |
23 | 25 | code?: string; // 编码 |
... | ... | @@ -61,9 +63,79 @@ export interface ListVO { |
61 | 63 | unit?: string; // 采购规格(配件展示) |
62 | 64 | splitUnit?: string; // 拆分件规格(配件展示) |
63 | 65 | splitCnt?: number; // 拆分件数量(配件展示) |
64 | - partType?:number | |
66 | + partType?:number, | |
67 | + planWaitListIds?:string | |
65 | 68 | } |
66 | 69 | |
70 | +export interface OutItem{ | |
71 | + /** | |
72 | + * 配件ID | |
73 | + */ | |
74 | +partId?:number; | |
75 | +/** | |
76 | + * 配件编码 | |
77 | + */ | |
78 | +partCode?:string; | |
79 | +/** | |
80 | + * 配件名称 | |
81 | + */ | |
82 | +partName?:string; | |
83 | +/** | |
84 | + * 出库数量 | |
85 | + */ | |
86 | +partCnt?:number; | |
87 | +/** | |
88 | + * 成本价 | |
89 | + */ | |
90 | +costPrice?:number; | |
91 | +/** | |
92 | + * 出库类型1工单2装潢 | |
93 | + */ | |
94 | +type?:number; | |
95 | +/** | |
96 | + * 出库类型ID | |
97 | + */ | |
98 | +typeId?:string; | |
99 | +/** | |
100 | + * 服务站id | |
101 | + */ | |
102 | +shopId?:number; | |
103 | +/** | |
104 | + * 服务站名称 | |
105 | + */ | |
106 | +shopName?:string; | |
107 | +/** | |
108 | + * 集团ID | |
109 | + */ | |
110 | +groupId?:number; | |
111 | +/** | |
112 | + * 出库时间 | |
113 | + */ | |
114 | +outTime?:string; | |
115 | + | |
116 | +/** | |
117 | + * 类型名称 | |
118 | + */ | |
119 | +typeName?:string; | |
120 | +} | |
121 | +export interface outListVO { | |
122 | + /** | |
123 | + * 时间段 | |
124 | + */ | |
125 | + date?: string; | |
126 | + /** | |
127 | + * 出库数量 | |
128 | + */ | |
129 | + outCnt?: number; | |
130 | + /** | |
131 | + * 最小时间 | |
132 | + */ | |
133 | + min?: string; | |
134 | + /** | |
135 | + * 最大时间 | |
136 | + */ | |
137 | + max?: string; | |
138 | +} | |
67 | 139 | /** |
68 | 140 | * 查询计划池列表 |
69 | 141 | */ |
... | ... | @@ -74,6 +146,19 @@ export function getList(params: Params): http.PromiseResp<ListVO[]> { |
74 | 146 | export function deleteApi(params: {poolId: number}) { |
75 | 147 | return request.post(`${PMS_HOST}/erp/plan/pool/delete/plan`, {...params}); |
76 | 148 | } |
149 | + | |
77 | 150 | export function editApi(params: { poolId?: number, partCnt?: number}) { |
78 | 151 | return request.post(`${PMS_HOST}/erp/plan/pool/update/plan`, {...params}); |
79 | 152 | } |
153 | + | |
154 | +export function outFlowApi(params: { poolIds?: any, planId?: number}):http.PromiseResp<outListVO[]> { | |
155 | + return request.get(`${PMS_HOST}/erp/storage/out/list`, {params}); | |
156 | +} | |
157 | + | |
158 | +export function outFlowDetailApi(params:any):http.PromiseResp<OutItem[]> { | |
159 | + return request.get(`${PMS_HOST}/erp/storage/out/detail`, {params}); | |
160 | +} | |
161 | + | |
162 | +export function custBuyApi(params: { planWaitListIds?: any}):http.PromiseResp<PartVO[]> { | |
163 | + return request.get(`${PMS_HOST}/erp/cus/buy/part/get/buy/part/list`, {params}); | |
164 | +} | ... | ... |
src/pages/pms/partPlan/PlanPool/components/AreaTable.tsx
... | ... | @@ -4,6 +4,7 @@ import useInitial from "@/hooks/useInitail"; |
4 | 4 | import {getList, ListVO} from "@/pages/pms/partPlan/PlanPool/api"; |
5 | 5 | import DetailModal from './DetailModal'; |
6 | 6 | import {useStore} from "@/pages/pms/partPlan/PlanPool"; |
7 | +import _ from 'lodash'; | |
7 | 8 | |
8 | 9 | const { Column } = Table; |
9 | 10 | interface Props { |
... | ... | @@ -14,7 +15,7 @@ interface Props { |
14 | 15 | id?: number, // 上一个列表ID |
15 | 16 | } |
16 | 17 | export default function Index(props: Props = {}) { |
17 | - const { dfParams, key } = useStore(); | |
18 | + const { dfParams, key, setItem, setOutVisible, setCustVisible } = useStore(); | |
18 | 19 | const {showAnalyse=true} = props; |
19 | 20 | const { data: parts, setParams, loading } = useInitial(getList, [], {...dfParams, ...props}); |
20 | 21 | const [visible, setVisible] = useState(false); |
... | ... | @@ -23,7 +24,6 @@ export default function Index(props: Props = {}) { |
23 | 24 | const [id, setId] = useState<number>(); |
24 | 25 | |
25 | 26 | useEffect(() => { |
26 | - console.log('dfParams11', dfParams, key, props); | |
27 | 27 | if (key == props.type) { |
28 | 28 | setParams(dfParams, true); |
29 | 29 | } |
... | ... | @@ -34,16 +34,16 @@ export default function Index(props: Props = {}) { |
34 | 34 | <Table rowKey={(v: ListVO) => `${v.id}`} scroll={{y: 500, x: 2500}} dataSource={parts || []} pagination={false} loading={loading}> |
35 | 35 | <Column title="区域库" dataIndex="name" fixed="left" /> |
36 | 36 | <Column title="本次计划数量(个)" dataIndex="cnt" fixed="left" /> |
37 | - <Column title="计划前库销比" dataIndex="ratio" render={t => (t || 0).toFixed(2)} /> | |
38 | - <Column title="计划后库销比" dataIndex="planeRatio" render={t => (t || 0).toFixed(2)} /> | |
37 | + <Column title="计划前库销比" dataIndex="ratio" render={t => (t ? t.toFixed(2) : "--")} /> | |
38 | + <Column title="计划后库销比" dataIndex="planRatio" render={t => (t ? t.toFixed(2) : "--")} /> | |
39 | 39 | <Column title="本次计划金额(元)" dataIndex="thisTimeAmount" /> |
40 | - <Column title="客户订件数量(个)" dataIndex="buyCnt" /> | |
40 | + <Column title="客户订件数量(个)" render={r => (r.buyCnt ? <a onClick={() => { setCustVisible(true); setItem(r); }}>{r.buyCnt}</a> : r.buyCnt)} /> | |
41 | 41 | <Column title="客户订件金额(元)" dataIndex="buyAmount" /> |
42 | 42 | <Column title="在途未锁(个)" dataIndex="onTheWayUnlockCnt" /> |
43 | 43 | |
44 | 44 | <Column title="在库未锁(个)" dataIndex="storageUnlockCnt" /> |
45 | 45 | <Column title="在库已锁(个)" dataIndex="storageLockedCnt" /> |
46 | - <Column title="滚动90天出库(个)" dataIndex="outStockCnt" /> | |
46 | + <Column title="近90天月均出库(个)" render={r => (r.outStockCnt ? <a onClick={() => { setOutVisible(true); setItem(r); }}>{_.floor(r.outStockCnt/3)}</a> : r.outStockCnt)} /> | |
47 | 47 | {showAnalyse && ( |
48 | 48 | <> |
49 | 49 | <Column | ... | ... |
src/pages/pms/partPlan/PlanPool/components/CustBuyModal.tsx
0 → 100644
1 | +import React, { useEffect, useState } from 'react'; | |
2 | +import { Button, Modal, Table } from 'antd'; | |
3 | +import { useStore } from '../index'; | |
4 | +import { custBuyApi } from '../api'; | |
5 | +import { PartVO } from '@/pages/pms/partPlan/CustBuyPlan/api'; | |
6 | +import useInitial from '@/hooks/useInitail'; | |
7 | + | |
8 | +const { Column } = Table; | |
9 | + | |
10 | +export default function Index() { | |
11 | + const { custVisible, setCustVisible, item } = useStore(); | |
12 | + const [delay, setDelay] = useState(true); | |
13 | + const { data, setParams, loading } = useInitial(custBuyApi, [], {}, delay); | |
14 | + | |
15 | + useEffect(() => { | |
16 | + if (custVisible && item?.planWaitListIds) { | |
17 | + setParams({ planWaitListIds: item.planWaitListIds }, true); | |
18 | + setDelay(false); | |
19 | + } | |
20 | + }, [custVisible]); | |
21 | + | |
22 | + return ( | |
23 | + <Modal | |
24 | + title="客户订件详情" | |
25 | + width={1100} | |
26 | + visible={custVisible} | |
27 | + maskClosable={false} | |
28 | + onCancel={() => setCustVisible(false)} | |
29 | + footer={[ | |
30 | + <Button onClick={() => setCustVisible(false)}>取消</Button> | |
31 | + ]} | |
32 | + > | |
33 | + <Table | |
34 | + rowKey={(v: PartVO) => `${v.waitListIds}`} | |
35 | + dataSource={data} | |
36 | + pagination={false} | |
37 | + loading={loading} | |
38 | + scroll={{x: 1400, y: 500}} | |
39 | + > | |
40 | + <Column title="时间" dataIndex="planTime" /> | |
41 | + <Column title="VIN" dataIndex="typeId" /> | |
42 | + <Column title="工单号" dataIndex="remark" /> | |
43 | + <Column title="车牌号" dataIndex="plateNo" /> | |
44 | + <Column title="配件编码" dataIndex="partCode" /> | |
45 | + <Column title="配件名称" dataIndex="partName" /> | |
46 | + <Column title="接车服务顾问" dataIndex="userName" /> | |
47 | + <Column title="门店名称" dataIndex="shopName" /> | |
48 | + <Column title="库房名称" dataIndex="storageName" /> | |
49 | + <Column title="订件数量" dataIndex="splitCnt" /> | |
50 | + <Column title="采购单价" dataIndex="price" /> | |
51 | + <Column title="采购数量" dataIndex="cnt" /> | |
52 | + </Table> | |
53 | + </Modal> | |
54 | + ); | |
55 | +} | |
0 | 56 | \ No newline at end of file | ... | ... |
src/pages/pms/partPlan/PlanPool/components/OutFlowDetailModal.tsx
0 → 100644
1 | +import React, { useEffect, useState } from 'react'; | |
2 | +import { Button, Modal, Table } from 'antd'; | |
3 | +import { outFlowDetailApi, OutItem } from '../api'; | |
4 | +import useInitial from '@/hooks/useInitail'; | |
5 | +import moment from 'moment'; | |
6 | + | |
7 | +const { Column } = Table; | |
8 | +interface Props { | |
9 | + visible?:boolean | |
10 | + setVisible:Function | |
11 | + item?:any | |
12 | +} | |
13 | +export default function Index(props:Props) { | |
14 | + const { visible, setVisible, item } = props; | |
15 | + const [delay, setDelay] = useState(true); | |
16 | + const { data, setParams, loading } = useInitial(outFlowDetailApi, [], {}, delay); | |
17 | + | |
18 | + useEffect(() => { | |
19 | + if (visible && item?.poolIds) { | |
20 | + setParams({ min: undefined, max: undefined, date: undefined, outCnt: undefined, ...item }, true); | |
21 | + setDelay(false); | |
22 | + } else { | |
23 | + setVisible(); | |
24 | + } | |
25 | + }, [visible]); | |
26 | + | |
27 | + return ( | |
28 | + <Modal | |
29 | + title="出库详情" | |
30 | + width={1000} | |
31 | + visible={visible} | |
32 | + maskClosable={false} | |
33 | + onCancel={() => setVisible()} | |
34 | + footer={[ | |
35 | + <Button onClick={() => setVisible()}>取消</Button> | |
36 | + ]} | |
37 | + > | |
38 | + <Table | |
39 | + dataSource={data} | |
40 | + loading={loading} | |
41 | + rowKey={(v: OutItem) => `${v.partId}`} | |
42 | + pagination={false} | |
43 | + scroll={{ y: 400 }} | |
44 | + > | |
45 | + <Column title="配件名称" dataIndex="partName" /> | |
46 | + <Column title="配件编码" dataIndex="partCode" /> | |
47 | + <Column title="出库数量" dataIndex="partCnt" /> | |
48 | + <Column title="出库类型" dataIndex="typeName" /> | |
49 | + <Column title="服务站名称" dataIndex="shopName" /> | |
50 | + <Column title="出库时间" render={r => moment(r.outTime).format('YYYY-MM-DD HH:mm:ss')} /> | |
51 | + </Table> | |
52 | + </Modal> | |
53 | + ); | |
54 | +} | |
0 | 55 | \ No newline at end of file | ... | ... |
src/pages/pms/partPlan/PlanPool/components/OutFlowModal.tsx
0 → 100644
1 | +import React, { useEffect, useState } from 'react'; | |
2 | +import {Button, Modal, Table} from 'antd'; | |
3 | +import {useStore} from '../index'; | |
4 | +import { outFlowApi, outListVO } from '../api'; | |
5 | +import useInitial from '@/hooks/useInitail'; | |
6 | +import OutFlowDetailModal from './OutFlowDetailModal'; | |
7 | + | |
8 | +const {Column} = Table; | |
9 | + | |
10 | +export default function Index() { | |
11 | + const {outVisible, setOutVisible, item} = useStore(); | |
12 | + const [delay, setDelay]= useState(true); | |
13 | + const { data, setParams, loading } = useInitial(outFlowApi, [], {}, delay); | |
14 | + const [current, setCurrent] = useState<{visible?: boolean, itemData?: any}>(); | |
15 | + | |
16 | + useEffect(() => { | |
17 | + if (outVisible && item?.poolIds) { | |
18 | + setParams({poolIds: item.poolIds}, true); | |
19 | + setDelay(false); | |
20 | + } | |
21 | + }, [outVisible]); | |
22 | + const total = data | |
23 | + .map(i => i.outCnt) | |
24 | + .reduce((prev, cur) => { | |
25 | + return (prev || 0) + (cur || 0); | |
26 | + }, 0); | |
27 | + | |
28 | + return ( | |
29 | + <Modal | |
30 | + title={`${item?.partName}滚动90天出库流水`} | |
31 | + width={1000} | |
32 | + visible={outVisible} | |
33 | + maskClosable={false} | |
34 | + onCancel={() => setOutVisible(false)} | |
35 | + footer={[ | |
36 | + <Button onClick={() => setOutVisible(false)}>取消</Button> | |
37 | + ]} | |
38 | + > | |
39 | + <div style={{marginBottom: 20}}> | |
40 | + 滚动90天出库数: | |
41 | + <span | |
42 | + style={{ color: '#0000FF', cursor: 'pointer' }} | |
43 | + onClick={() => setCurrent({ visible: true, itemData: { poolIds: item?.poolIds } })} | |
44 | + > | |
45 | + {total} | |
46 | + </span> | |
47 | + </div> | |
48 | + <Table | |
49 | + dataSource={data} | |
50 | + loading={loading} | |
51 | + rowKey={(v: outListVO) => `${v.date}`} | |
52 | + pagination={false} | |
53 | + scroll={{y: 400}} | |
54 | + > | |
55 | + <Column title="出库日期" dataIndex="date" /> | |
56 | + <Column title="出库数量" render={r => <a onClick={() => setCurrent({ visible: true, itemData: { ...r, poolIds: item?.poolIds }})}>{r.outCnt}</a>} /> | |
57 | + </Table> | |
58 | + <OutFlowDetailModal visible={current?.visible} setVisible={() => setCurrent({visible: false, itemData: undefined})} item={current?.itemData} /> | |
59 | + </Modal> | |
60 | + ); | |
61 | +} | |
0 | 62 | \ No newline at end of file | ... | ... |
src/pages/pms/partPlan/PlanPool/components/PartTable.tsx
... | ... | @@ -4,6 +4,7 @@ import useInitial from "@/hooks/useInitail"; |
4 | 4 | import {getList, ListVO} from "@/pages/pms/partPlan/PlanPool/api"; |
5 | 5 | import DetailModal from './DetailModal'; |
6 | 6 | import {useStore} from "@/pages/pms/partPlan/PlanPool"; |
7 | +import _ from 'lodash'; | |
7 | 8 | |
8 | 9 | const { Column } = Table; |
9 | 10 | interface Props { |
... | ... | @@ -16,7 +17,7 @@ interface Props { |
16 | 17 | id?: number, // 上一个列表ID |
17 | 18 | } |
18 | 19 | export default function Index(props: Props = {}) { |
19 | - const { dfParams, key } = useStore(); | |
20 | + const { dfParams, key, setItem, setOutVisible, setCustVisible } = useStore(); | |
20 | 21 | const {showAnalyse=true} = props; |
21 | 22 | const { data: parts, setParams, loading } = useInitial(getList, [], {...dfParams, ...props}); |
22 | 23 | const [visible, setVisible] = useState(false); |
... | ... | @@ -39,17 +40,17 @@ export default function Index(props: Props = {}) { |
39 | 40 | <Column title="来源类型" dataIndex="typeName" /> |
40 | 41 | <Column title="配件类型" dataIndex="partTypeName" /> |
41 | 42 | |
42 | - <Column title="计划前库销比" dataIndex="ratio" render={t => (t || 0).toFixed(2)} /> | |
43 | - <Column title="计划后库销比" dataIndex="planeRatio" render={t => (t || 0).toFixed(2)} /> | |
43 | + <Column title="计划前库销比" dataIndex="ratio" render={t => (t ? t.toFixed(2) : "--")} /> | |
44 | + <Column title="计划后库销比" dataIndex="planRatio" render={t => (t ? t.toFixed(2) : "--")} /> | |
44 | 45 | |
45 | 46 | <Column title="本次计划金额(元)" dataIndex="thisTimeAmount" /> |
46 | - <Column title="客户订件数量(个)" dataIndex="buyCnt" /> | |
47 | + <Column title="客户订件数量(个)" render={r => (r.buyCnt ? <a onClick={() => { setCustVisible(true); setItem(r); }}>{r.buyCnt}</a> : r.buyCnt)} /> | |
47 | 48 | <Column title="客户订件金额(元)" dataIndex="buyAmount" /> |
48 | 49 | <Column title="在途未锁(个)" dataIndex="onTheWayUnlockCnt" /> |
49 | 50 | |
50 | 51 | <Column title="在库未锁(个)" dataIndex="storageUnlockCnt" /> |
51 | 52 | <Column title="在库已锁(个)" dataIndex="storageLockedCnt" /> |
52 | - <Column title="滚动90天出库(个)" dataIndex="outStockCnt" /> | |
53 | + <Column title="近90天月均出库(个)" render={r => (r.outStockCnt ? <a onClick={() => { setOutVisible(true); setItem(r); }}>{_.floor(r.outStockCnt/3)}</a> : r.outStockCnt)} /> | |
53 | 54 | {showAnalyse && ( |
54 | 55 | <> |
55 | 56 | <Column | ... | ... |
src/pages/pms/partPlan/PlanPool/components/SeriesTable.tsx
... | ... | @@ -4,6 +4,7 @@ import useInitial from "@/hooks/useInitail"; |
4 | 4 | import {getList, ListVO} from "@/pages/pms/partPlan/PlanPool/api"; |
5 | 5 | import DetailModal from './DetailModal'; |
6 | 6 | import {useStore} from "@/pages/pms/partPlan/PlanPool"; |
7 | +import _ from 'lodash'; | |
7 | 8 | |
8 | 9 | const { Column } = Table; |
9 | 10 | interface Props { |
... | ... | @@ -15,7 +16,7 @@ interface Props { |
15 | 16 | id?: number, // 上一个列表ID |
16 | 17 | } |
17 | 18 | export default function Index(props: Props = {}) { |
18 | - const { dfParams, key } = useStore(); | |
19 | + const { dfParams, key, setItem, setOutVisible, setCustVisible } = useStore(); | |
19 | 20 | const {showAnalyse=true} = props; |
20 | 21 | const { data: parts, setParams, loading } = useInitial(getList, [], {...dfParams, ...props}); |
21 | 22 | const [visible, setVisible] = useState(false); |
... | ... | @@ -34,17 +35,17 @@ export default function Index(props: Props = {}) { |
34 | 35 | <Table rowKey={(v: ListVO) => `${v.id}`} scroll={{y: 500, x: 2500}} dataSource={parts || []} pagination={false} loading={loading}> |
35 | 36 | <Column title="车系" dataIndex="name" fixed="left" /> |
36 | 37 | <Column title="本次计划数量(个)" dataIndex="cnt" fixed="left" /> |
37 | - <Column title="计划前库销比" dataIndex="ratio" render={t => (t || 0).toFixed(2)} /> | |
38 | - <Column title="计划后库销比" dataIndex="planeRatio" render={t => (t || 0).toFixed(2)} /> | |
38 | + <Column title="计划前库销比" dataIndex="ratio" render={t => (t ? t.toFixed(2) : "--")} /> | |
39 | + <Column title="计划后库销比" dataIndex="planRatio" render={t => (t ? t.toFixed(2) : "--")} /> | |
39 | 40 | |
40 | 41 | <Column title="本次计划金额(元)" dataIndex="thisTimeAmount" /> |
41 | - <Column title="客户订件数量(个)" dataIndex="buyCnt" /> | |
42 | + <Column title="客户订件数量(个)" render={r => (r.buyCnt ? <a onClick={() => { setCustVisible(true); setItem(r); }}>{r.buyCnt}</a> : r.buyCnt)} /> | |
42 | 43 | <Column title="客户订件金额(元)" dataIndex="buyAmount" /> |
43 | 44 | <Column title="在途未锁(个)" dataIndex="onTheWayUnlockCnt" /> |
44 | 45 | |
45 | 46 | <Column title="在库未锁(个)" dataIndex="storageUnlockCnt" /> |
46 | 47 | <Column title="在库已锁(个)" dataIndex="storageLockedCnt" /> |
47 | - <Column title="滚动90天出库(个)" dataIndex="outStockCnt" /> | |
48 | + <Column title="近90天月均出库(个)" render={r => (r.outStockCnt ? <a onClick={() => { setOutVisible(true); setItem(r); }}>{_.floor(r.outStockCnt/3)}</a> : r.outStockCnt)} /> | |
48 | 49 | {showAnalyse && ( |
49 | 50 | <> |
50 | 51 | <Column | ... | ... |
src/pages/pms/partPlan/PlanPool/components/StoragePartTable.tsx
... | ... | @@ -4,6 +4,7 @@ import useInitial from "@/hooks/useInitail"; |
4 | 4 | import {getList, ListVO} from "@/pages/pms/partPlan/PlanPool/api"; |
5 | 5 | import {useStore} from "@/pages/pms/partPlan/PlanPool"; |
6 | 6 | import {deleteApi, editApi} from '../api'; |
7 | +import _ from 'lodash'; | |
7 | 8 | |
8 | 9 | const { Column } = Table; |
9 | 10 | const {Item} = Form; |
... | ... | @@ -12,11 +13,11 @@ interface Props { |
12 | 13 | type?: number, // 类型1区域库2库房3车系4车型5配件 |
13 | 14 | } |
14 | 15 | export default function Index(props: Props = {}) { |
15 | - const { dfParams, key } = useStore(); | |
16 | + const { dfParams, key, setItem, setOutVisible, setCustVisible } = useStore(); | |
16 | 17 | const [form] = Form.useForm(); |
17 | 18 | const { data: parts, setParams, loading } = useInitial(getList, [], dfParams); |
18 | 19 | const [visible, setVisible] = useState(false); |
19 | - const [item, setItem] = useState<ListVO>(); | |
20 | + const [itemPrice, setItemPrice] = useState<ListVO>(); | |
20 | 21 | |
21 | 22 | useEffect(() => { |
22 | 23 | if (key == props.type) { |
... | ... | @@ -25,8 +26,8 @@ export default function Index(props: Props = {}) { |
25 | 26 | }, [dfParams, key]); |
26 | 27 | |
27 | 28 | useEffect(() => { |
28 | - if (visible && item?.poolId) { | |
29 | - form.setFieldsValue({cnt: item.cnt}); | |
29 | + if (visible && itemPrice?.poolId) { | |
30 | + form.setFieldsValue({ cnt: itemPrice.cnt}); | |
30 | 31 | } else { |
31 | 32 | form.resetFields; |
32 | 33 | } |
... | ... | @@ -43,7 +44,7 @@ export default function Index(props: Props = {}) { |
43 | 44 | fixed="left" |
44 | 45 | render={r => ( |
45 | 46 | <> |
46 | - <a onClick={() => { setVisible(true); setItem(r); }}>编辑</a> | |
47 | + <a onClick={() => { setVisible(true); setItemPrice(r); }}>编辑</a> | |
47 | 48 | <Divider type="vertical" /> |
48 | 49 | <Popconfirm |
49 | 50 | title="确认删除?" |
... | ... | @@ -64,16 +65,16 @@ export default function Index(props: Props = {}) { |
64 | 65 | <Column title="来源类型" dataIndex="typeName" /> |
65 | 66 | <Column title="配件类型" dataIndex="partTypeName" /> |
66 | 67 | |
67 | - <Column title="计划前库销比" dataIndex="ratio" render={t => (t || 0).toFixed(2)} /> | |
68 | - <Column title="计划后库销比" dataIndex="planeRatio" render={t => (t || 0).toFixed(2)} /> | |
68 | + <Column title="计划前库销比" dataIndex="ratio" render={t => (t ? t.toFixed(2) : "--")} /> | |
69 | + <Column title="计划后库销比" dataIndex="planRatio" render={t => (t ? t.toFixed(2) : "--")} /> | |
69 | 70 | |
70 | 71 | <Column title="本次计划金额(元)" dataIndex="thisTimeAmount" /> |
71 | - <Column title="客户订件数量(个)" dataIndex="buyCnt" /> | |
72 | + <Column title="客户订件数量(个)" render={r => (r.buyCnt ? <a onClick={() => { setCustVisible(true); setItem(r); }}>{r.buyCnt}</a> : r.buyCnt)} /> | |
72 | 73 | <Column title="客户订件金额(元)" dataIndex="buyAmount" /> |
73 | 74 | <Column title="在途未锁(个)" dataIndex="onTheWayUnlockCnt" /> |
74 | 75 | <Column title="在库未锁(个)" dataIndex="storageUnlockCnt" /> |
75 | 76 | <Column title="在库已锁(个)" dataIndex="storageLockedCnt" /> |
76 | - <Column title="滚动90天出库(个)" dataIndex="outStockCnt" /> | |
77 | + <Column title="近90天月均出库(个)" render={r => (r.outStockCnt ? <a onClick={() => { setOutVisible(true); setItem(r); }}>{_.floor(r.outStockCnt/3)}</a> : r.outStockCnt)} /> | |
77 | 78 | |
78 | 79 | </Table> |
79 | 80 | <Modal |
... | ... | @@ -90,7 +91,7 @@ export default function Index(props: Props = {}) { |
90 | 91 | htmlType="submit" |
91 | 92 | onClick={() => { |
92 | 93 | form.validateFields().then(fields => { |
93 | - editApi({poolId: item?.poolId, partCnt: fields.cnt}).then(res => { | |
94 | + editApi({ poolId: itemPrice?.poolId, partCnt: fields.cnt}).then(res => { | |
94 | 95 | setVisible(false); |
95 | 96 | message.success("操作成功"); |
96 | 97 | setParams({}, true); | ... | ... |
src/pages/pms/partPlan/PlanPool/components/StorageTable.tsx
... | ... | @@ -4,6 +4,7 @@ import useInitial from "@/hooks/useInitail"; |
4 | 4 | import {getList, ListVO} from "@/pages/pms/partPlan/PlanPool/api"; |
5 | 5 | import DetailModal from './DetailModal'; |
6 | 6 | import {useStore} from "@/pages/pms/partPlan/PlanPool"; |
7 | +import {floor} from 'lodash'; | |
7 | 8 | |
8 | 9 | const { Column } = Table; |
9 | 10 | interface Props { |
... | ... | @@ -14,7 +15,7 @@ interface Props { |
14 | 15 | id?: number, // 上一个列表ID |
15 | 16 | } |
16 | 17 | export default function Index(props: Props = {}) { |
17 | - const { dfParams, key } = useStore(); | |
18 | + const { dfParams, key, setItem, setOutVisible, setCustVisible } = useStore(); | |
18 | 19 | const {showAnalyse=true} = props; |
19 | 20 | const { data: parts, setParams, loading } = useInitial(getList, [], {...dfParams, ...props}); |
20 | 21 | const [visible, setVisible] = useState(false); |
... | ... | @@ -33,16 +34,16 @@ export default function Index(props: Props = {}) { |
33 | 34 | <Table rowKey={(v: ListVO) => `${v.id}`} scroll={{y: 500, x: 2500}} dataSource={parts || []} pagination={false} loading={loading}> |
34 | 35 | <Column title="库房" dataIndex="name" fixed="left" /> |
35 | 36 | <Column title="本次计划数量(个)" dataIndex="cnt" fixed="left" /> |
36 | - <Column title="计划前库销比" dataIndex="ratio" render={t => (t || 0).toFixed(2)} /> | |
37 | - <Column title="计划后库销比" dataIndex="planeRatio" render={t => (t || 0).toFixed(2)} /> | |
37 | + <Column title="计划前库销比" dataIndex="ratio" render={t => (t ? t.toFixed(2) : "--")} /> | |
38 | + <Column title="计划后库销比" dataIndex="planRatio" render={t => (t ? t.toFixed(2) : "--")} /> | |
38 | 39 | |
39 | 40 | <Column title="本次计划金额(元)" dataIndex="thisTimeAmount" /> |
40 | - <Column title="客户订件数量(个)" dataIndex="buyCnt" /> | |
41 | + <Column title="客户订件数量(个)" render={r => (r.buyCnt ? <a onClick={() => { setCustVisible(true); setItem(r); }}>{r.buyCnt}</a> : r.buyCnt)} /> | |
41 | 42 | <Column title="客户订件金额(元)" dataIndex="buyAmount" /> |
42 | 43 | <Column title="在途未锁(个)" dataIndex="onTheWayUnlockCnt" /> |
43 | 44 | <Column title="在库未锁(个)" dataIndex="storageUnlockCnt" /> |
44 | 45 | <Column title="在库已锁(个)" dataIndex="storageLockedCnt" /> |
45 | - <Column title="滚动90天出库(个)" dataIndex="outStockCnt" /> | |
46 | + <Column title="近90天月均出库(个)" render={r => (r.outStockCnt ? <a onClick={() => { setOutVisible(true); setItem(r); }}>{floor(r.outStockCnt/3)}</a> : r.outStockCnt)} /> | |
46 | 47 | {showAnalyse && ( |
47 | 48 | <> |
48 | 49 | <Column | ... | ... |
src/pages/pms/partPlan/PlanPool/index.tsx
... | ... | @@ -11,6 +11,8 @@ import st from "@/pages/pms/partPlan/PlanShipping/style.less"; |
11 | 11 | import Filter from './components/Filter'; |
12 | 12 | import {createStore} from "@/hooks/moz"; |
13 | 13 | import store from "./useStore"; |
14 | +import OutFlowModal from './components/OutFlowModal'; | |
15 | +import CustBuyModal from './components/CustBuyModal'; | |
14 | 16 | |
15 | 17 | export const { Provider, useStore } = createStore(store); |
16 | 18 | |
... | ... | @@ -38,6 +40,8 @@ function Index() { |
38 | 40 | <StoragePartTable type={6} /> |
39 | 41 | </Tabs.TabPane> |
40 | 42 | </Tabs> |
43 | + <OutFlowModal /> | |
44 | + <CustBuyModal /> | |
41 | 45 | </Card> |
42 | 46 | </ConfigProvider> |
43 | 47 | </PageHeaderWrapper> | ... | ... |
src/pages/pms/partPlan/PlanPool/useStore.ts
1 | 1 | import { useState } from 'react'; |
2 | 2 | import useInitail from "@/hooks/useInitail"; |
3 | 3 | import { getPartTypeApi } from '@/pages/pms/part/Repertory/api'; |
4 | +import {ListVO} from './api'; | |
4 | 5 | |
5 | 6 | export default function useStore() { |
6 | 7 | const [dfParams, setDfParams] = useState<any>({}); |
7 | 8 | const [key, setKey] = useState<any>('1'); |
8 | 9 | const { data: partTypeData } = useInitail(getPartTypeApi, [], {}); |
10 | + const [outVisible, setOutVisible] = useState(false); | |
11 | + const [custVisible, setCustVisible] = useState(false); | |
12 | + const [item, setItem] = useState<ListVO>(); | |
9 | 13 | |
10 | 14 | return { |
11 | 15 | dfParams, |
12 | 16 | setDfParams, |
13 | 17 | key, |
14 | 18 | setKey, |
15 | - partTypeData | |
19 | + partTypeData, | |
20 | + outVisible, | |
21 | + setOutVisible, | |
22 | + item, | |
23 | + setItem, | |
24 | + custVisible, | |
25 | + setCustVisible, | |
16 | 26 | }; |
17 | 27 | } | ... | ... |
src/pages/pms/storage/partShop/api.ts
... | ... | @@ -42,6 +42,12 @@ export function getLockDetail(params?: PmsStoragePartShop.LockParams): http.Prom |
42 | 42 | export function getFlowDetail(params?: PmsStoragePartShop.FlowParams): http.PromisePageResp<PmsStoragePartShop.FlowVO> { |
43 | 43 | return request.get(`${PMS_HOST}/erp/part/shop/get/record`, { params }); |
44 | 44 | } |
45 | +/** | |
46 | + * 查询门店备件锁库流水明细列表 | |
47 | + */ | |
48 | +export function getLockFlowDetail(params?: PmsStoragePartShop.FlowParams): http.PromisePageResp<PmsStoragePartShop.FlowVO> { | |
49 | + return request.get(`${PMS_HOST}/erp/part/shop/get/lock/record`, { params }); | |
50 | +} | |
45 | 51 | // 释放库存 |
46 | 52 | export function unLock(params: PmsStoragePartShop.unLock): http.PromiseResp<string> { |
47 | 53 | return request.post(`${PMS_HOST}/erp/part/shop/cancel/lock`, { ...params }, { contentType: "form-urlencoded" }); | ... | ... |
src/pages/pms/storage/partShop/components/Filter.tsx
... | ... | @@ -32,14 +32,14 @@ export default function Filter() { |
32 | 32 | return ( |
33 | 33 | <div> |
34 | 34 | <Search |
35 | - style={{ width: 200, marginRight: 10 }} | |
35 | + style={{ width: 180, marginRight: 10, marginBottom: 10 }} | |
36 | 36 | allowClear |
37 | 37 | enterButton |
38 | 38 | placeholder="配件编码|名称" |
39 | 39 | onSearch={v => handleChangeKeywords(v)} |
40 | 40 | /> |
41 | 41 | <PmsSelect |
42 | - style={{ width: 180, marginRight: 10}} | |
42 | + style={{ width: 180, marginRight: 10, marginBottom: 10 }} | |
43 | 43 | allowClear |
44 | 44 | value={innerParams.shopId} |
45 | 45 | onChange={value => { |
... | ... | @@ -52,7 +52,7 @@ export default function Filter() { |
52 | 52 | .map((item: PmsStoragePartShop.Option) => ({value: item.id, label: item.name}))} |
53 | 53 | /> |
54 | 54 | <PmsSelect |
55 | - style={{ width: 150, marginRight: 10}} | |
55 | + style={{ width: 180, marginRight: 10, marginBottom: 10 }} | |
56 | 56 | allowClear |
57 | 57 | value={innerParams.storageId} |
58 | 58 | onChange={value => { |
... | ... | @@ -66,7 +66,7 @@ export default function Filter() { |
66 | 66 | /> |
67 | 67 | <PmsSelect |
68 | 68 | allowClear |
69 | - style={{ width: 150, marginRight: 10}} | |
69 | + style={{ width: 180, marginRight: 10, marginBottom: 10 }} | |
70 | 70 | onChange={(type) => { |
71 | 71 | setParams({ ...innerParams, current: 1, type }, true); |
72 | 72 | }} |
... | ... | @@ -77,7 +77,7 @@ export default function Filter() { |
77 | 77 | allowClear |
78 | 78 | placeholder="有无库存筛选" |
79 | 79 | onChange={v => isStock(v)} |
80 | - style={{ width: 150, marginRight: 10 }} | |
80 | + style={{ width: 180, marginRight: 10, marginBottom: 10 }} | |
81 | 81 | options={[ |
82 | 82 | {value: 1, label: "有库存"}, |
83 | 83 | {value: 0, label: "无库存"}, |
... | ... | @@ -87,7 +87,7 @@ export default function Filter() { |
87 | 87 | placeholder="排序方式" |
88 | 88 | defaultValue={18} |
89 | 89 | onChange={v => isSorter(v)} |
90 | - style={{ width: 150 }} | |
90 | + style={{ width: 180 }} | |
91 | 91 | options={[ |
92 | 92 | {value: 18, label: "总库存降序"}, |
93 | 93 | {value: 21, label: "锁定库存降序"}, | ... | ... |
src/pages/pms/storage/partShop/components/List.tsx
... | ... | @@ -4,6 +4,7 @@ import { useStore } from '../index'; |
4 | 4 | import LockDetailModal from './LockDetailModal'; |
5 | 5 | import FlowDetailModal from './FlowDetailModal'; |
6 | 6 | import PartShopModal from './PartShopModal'; |
7 | +import LockFlowModal from './LockFlowModal'; | |
7 | 8 | |
8 | 9 | const { Column } = Table; |
9 | 10 | |
... | ... | @@ -11,6 +12,7 @@ export default function Filter() { |
11 | 12 | const { setParams, loading, paginationConfig, list, setVisible, setItem, item, fw, setIsprice} = useStore(); |
12 | 13 | const [visibleLockDetail, setVisibleLockDetail] = useState(false); |
13 | 14 | const [visibleFlowDetail, setVisibleFlowDetail] = useState(false); |
15 | + const [visibleLockFlow, setVisibleLockFlow] = useState(false); | |
14 | 16 | |
15 | 17 | return ( |
16 | 18 | <div> |
... | ... | @@ -43,6 +45,14 @@ export default function Filter() { |
43 | 45 | title="入库流水" |
44 | 46 | render={(t, it: PartRepertorySpace.Item) => <a onClick={() => { setVisibleFlowDetail(true); setItem({...it, isOut: false}); }}>查看</a>} |
45 | 47 | /> |
48 | + <Column | |
49 | + title="锁库流水" | |
50 | + render={(t, it: PartRepertorySpace.Item) => <a onClick={() => { setVisibleLockFlow(true); setItem({...it, isLock: true}); }}>查看</a>} | |
51 | + /> | |
52 | + <Column | |
53 | + title="释放流水" | |
54 | + render={(t, it: PartRepertorySpace.Item) => <a onClick={() => { setVisibleLockFlow(true); setItem({...it, isLock: false}); }}>查看</a>} | |
55 | + /> | |
46 | 56 | {fw ? ( |
47 | 57 | <Column |
48 | 58 | title="操作" |
... | ... | @@ -65,6 +75,7 @@ export default function Filter() { |
65 | 75 | </Table> |
66 | 76 | <LockDetailModal item={item} visible={visibleLockDetail} onCancel={() => setVisibleLockDetail(false)} /> |
67 | 77 | <FlowDetailModal item={item} visible={visibleFlowDetail} onCancel={() => setVisibleFlowDetail(false)} /> |
78 | + <LockFlowModal item={item} visible={visibleLockFlow} onCancel={() => setVisibleLockFlow(false)} /> | |
68 | 79 | <PartShopModal /> |
69 | 80 | </div> |
70 | 81 | ); | ... | ... |
src/pages/pms/storage/partShop/components/LockFlowModal.tsx
0 → 100644
1 | +import React, {useEffect, useState} from 'react'; | |
2 | +import {Modal, Table, Button} from 'antd'; | |
3 | +import { getLockFlowDetail } from '@/pages/pms/storage/partShop/api'; | |
4 | +import usePagination from '@/hooks/usePagination'; | |
5 | + | |
6 | +interface Props { | |
7 | + item?: any | |
8 | + visible: boolean | |
9 | + onCancel: () => any | |
10 | +} | |
11 | +const {Column} = Table; | |
12 | +export default function Index(props: Props) { | |
13 | + const {item, visible, onCancel} = props; | |
14 | + const [delay, setDelay] = useState(true); | |
15 | + const { list, loading, setParams, paginationConfig, setList } = usePagination<PmsStoragePartShop.FlowVO>(getLockFlowDetail, {}, { delay }); | |
16 | + | |
17 | + useEffect(() => { | |
18 | + if (item.shopId && item.partId && visible) { | |
19 | + setParams({ partId: item.partId, shopId: item.shopId, isLock: item.isLock }, true); | |
20 | + setDelay(false); | |
21 | + } else { | |
22 | + setList([]); | |
23 | + } | |
24 | + }, [visible, item.partId]); | |
25 | + | |
26 | + return ( | |
27 | + <Modal | |
28 | + title={`${item.isLock ? '锁定库存' : '释放库存'}流水`} | |
29 | + width={1000} | |
30 | + visible={visible} | |
31 | + maskClosable={false} | |
32 | + onCancel={onCancel} | |
33 | + footer={[ | |
34 | + <Button key="1" onClick={onCancel}>取消</Button>, | |
35 | + ]} | |
36 | + > | |
37 | + <Table | |
38 | + loading={loading} | |
39 | + dataSource={list} | |
40 | + rowKey={(v: PmsStoragePartShop.FlowVO) => `${v.type}_${v.typeId}`} | |
41 | + pagination={paginationConfig} | |
42 | + > | |
43 | + <Column title="配件数量" dataIndex="partCnt" /> | |
44 | + <Column title="单号" dataIndex="remark" render={t => t || "--"} /> | |
45 | + <Column | |
46 | + title="详情" | |
47 | + dataIndex="text" | |
48 | + width={350} | |
49 | + render={(t: string, it: PmsStoragePartShop.FlowVO) => { | |
50 | + const obj = JSON.parse(t || '{}'); | |
51 | + return ( | |
52 | + <div> | |
53 | + {!!obj.type && <div>{`类型: ${obj.type}`}</div>} | |
54 | + {!!obj.serviceCatName && <div>{`进站类型: ${obj.serviceCatName}`}</div>} | |
55 | + {!!obj.exclusiveAdviser && <div>{`专属顾问: ${obj.exclusiveAdviser}`}</div>} | |
56 | + {!!obj.shopLinkmanName && <div>{`服务顾问: ${obj.shopLinkmanName}`}</div>} | |
57 | + {!!obj.receiverName && <div>{`服务顾问: ${obj.receiverName}`}</div>} | |
58 | + {!!obj.plateNo && <div>{`车牌号: ${obj.plateNo}`}</div>} | |
59 | + {!!obj.ownerName && <div>{`车主: ${obj.ownerName}`}</div>} | |
60 | + {!!obj.vin && <div>{`VIN: ${obj.vin}`}</div>} | |
61 | + {!!obj.userName && <div>{`操作账号: ${obj.userName}`}</div>} | |
62 | + {!!obj.fixRemark && <div>{`备注: ${obj.fixRemark}`}</div>} | |
63 | + </div> | |
64 | + ); | |
65 | + }} | |
66 | + /> | |
67 | + <Column title={`${item.isLock ? '锁库' : '释放'}时间`} dataIndex="recordTime" /> | |
68 | + </Table> | |
69 | + </Modal> | |
70 | + ); | |
71 | +} | |
0 | 72 | \ No newline at end of file | ... | ... |
src/pages/pms/storage/partShop/components/PartShopModal.tsx
... | ... | @@ -29,7 +29,7 @@ export default function PartShopModal() { |
29 | 29 | }); |
30 | 30 | } |
31 | 31 | }, [visible]); |
32 | - | |
32 | + | |
33 | 33 | useEffect(() => { |
34 | 34 | if (isadd) { |
35 | 35 | setItem({}); |
... | ... | @@ -119,7 +119,7 @@ export default function PartShopModal() { |
119 | 119 | <InputNumber style={{ width: "100%" }} min={0} step={1} placeholder="总库存数量" disabled={isprice || isadd} /> |
120 | 120 | </Item> |
121 | 121 | <Item label="锁定库存" name="lockStock" rules={[{ required: true, message: "请输入锁定库存" }, { validator: validatorLockStock }]}> |
122 | - <InputNumber style={{ width: "100%" }} min={0} step={1} placeholder="锁定库存" disabled={isprice || isadd} /> | |
122 | + <InputNumber style={{ width: "100%" }} min={0} step={1} placeholder="锁定库存" disabled /> | |
123 | 123 | </Item> |
124 | 124 | {!!fw && !isprice && !isadd && ( |
125 | 125 | <Item label="修改人" name="user" rules={[{ required: true, message: "请输入修改人" }]}> | ... | ... |
src/pages/pms/storage/partShop/index.tsx
1 | 1 | import React, {useState} from 'react'; |
2 | -import { PlusOutlined, VerticalAlignBottomOutlined } from '@ant-design/icons'; | |
2 | +import { PlusOutlined, DownloadOutlined, UploadOutlined } from '@ant-design/icons'; | |
3 | 3 | import { Card, Button } from 'antd'; |
4 | 4 | import { PageHeaderWrapper } from '@ant-design/pro-layout'; |
5 | 5 | import Filter from './components/Filter'; |
... | ... | @@ -20,53 +20,51 @@ function PartShop() { |
20 | 20 | return ( |
21 | 21 | <PageHeaderWrapper title={`服务站配件${fw ? '(霏微)': ''}`}> |
22 | 22 | <Card> |
23 | - <div style={{ display: 'flex', flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', marginBottom: 20 }}> | |
23 | + <div style={{ display: 'flex', flexDirection: 'row', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: 20 }}> | |
24 | 24 | <Filter /> |
25 | - <div style={{display: 'flex', flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center'}}> | |
25 | + <div style={{ display: 'flex', alignItems: 'center'}}> | |
26 | + <Button | |
27 | + type="primary" | |
28 | + icon={<PlusOutlined />} | |
29 | + onClick={() => { setVisible(true); setIsadd(true); }} | |
30 | + > | |
31 | + 新增 | |
32 | + </Button> | |
26 | 33 | {fw && ( |
27 | - <> | |
28 | - <Button | |
29 | - icon={(<PlusOutlined />)} | |
30 | - type="primary" | |
31 | - onClick={() => setImportVisible(true)} | |
32 | - > | |
33 | - 导入 | |
34 | - </Button> | |
35 | - <Button | |
36 | - icon={(<PlusOutlined />)} | |
37 | - type="primary" | |
38 | - onClick={() => { setImportVisible(true); setStock(true); }} | |
39 | - style={{marginLeft: 10}} | |
40 | - > | |
41 | - 调运库存导入 | |
42 | - </Button> | |
43 | - </> | |
44 | - )} | |
45 | - {/* {!fw && ( */} | |
46 | 34 | <Button |
35 | + icon={<UploadOutlined />} | |
47 | 36 | type="primary" |
48 | - icon={<VerticalAlignBottomOutlined />} | |
49 | - onClick={() => { setMore(true); setIslock(true); }} | |
37 | + onClick={() => setImportVisible(true)} | |
38 | + style={{ marginLeft: 10 }} | |
39 | + > | |
40 | + 导入 | |
41 | + </Button> | |
42 | + )} | |
43 | + {fw && ( | |
44 | + <Button | |
45 | + icon={<UploadOutlined />} | |
46 | + type="primary" | |
47 | + onClick={() => { setImportVisible(true); setStock(true); }} | |
50 | 48 | style={{marginLeft: 10}} |
51 | 49 | > |
52 | - 锁件批量导出 | |
50 | + 调运库存导入 | |
53 | 51 | </Button> |
54 | - {/* )} */} | |
52 | + )} | |
55 | 53 | <Button |
56 | 54 | type="primary" |
57 | - icon={<VerticalAlignBottomOutlined />} | |
58 | - onClick={() => setMore(true)} | |
55 | + icon={<DownloadOutlined />} | |
56 | + onClick={() => { setMore(true); setIslock(true); }} | |
59 | 57 | style={{marginLeft: 10}} |
60 | 58 | > |
61 | - 库存批量导出 | |
59 | + 锁件批量导出 | |
62 | 60 | </Button> |
63 | 61 | <Button |
64 | 62 | type="primary" |
65 | - icon={<PlusOutlined />} | |
66 | - onClick={() => { setVisible(true); setIsadd(true); }} | |
67 | - style={{ marginLeft: 10}} | |
63 | + icon={<DownloadOutlined />} | |
64 | + onClick={() => setMore(true)} | |
65 | + style={{marginLeft: 10}} | |
68 | 66 | > |
69 | - 新增 | |
67 | + 库存批量导出 | |
70 | 68 | </Button> |
71 | 69 | </div> |
72 | 70 | </div> | ... | ... |
src/pages/pms/storage/partShop/interface.d.ts
... | ... | @@ -21,7 +21,8 @@ declare namespace PmsStoragePartShop { |
21 | 21 | actualStock?: number, |
22 | 22 | shopId?: number, // 服务站ID |
23 | 23 | shopName?: string // 服务站名称 |
24 | - isOut?: boolean | |
24 | + isOut?: boolean, | |
25 | + isLock?:boolean | |
25 | 26 | } |
26 | 27 | |
27 | 28 | interface Params { |
... | ... | @@ -77,6 +78,7 @@ declare namespace PmsStoragePartShop { |
77 | 78 | isOut?: boolean, // 出入库 |
78 | 79 | current?: number |
79 | 80 | pageSize?:number |
81 | + isLock?:boolean // 锁库 ?释放 | |
80 | 82 | } |
81 | 83 | |
82 | 84 | /** | ... | ... |
src/pages/pms/transfer/HuolalaSetting/api.ts
0 → 100644
1 | +import { http } from '@/typing/http'; | |
2 | +import request from '@/utils/request'; | |
3 | +import { PMS_HOST, HOST } from '@/utils/host'; | |
4 | + | |
5 | +export interface ListVO{ | |
6 | + /** | |
7 | + * 财务账户信息id | |
8 | + */ | |
9 | + id?:number | |
10 | + /** | |
11 | + * 往来单位id | |
12 | + */ | |
13 | + supplierId?:number | |
14 | + /** | |
15 | + * 往来单位名称 | |
16 | + */ | |
17 | + supplierName?:string | |
18 | + /** | |
19 | + * 签约门店id | |
20 | + */ | |
21 | + shopId?:number | |
22 | + /** | |
23 | + * 签约门店名称 | |
24 | + */ | |
25 | + shopName?:string; | |
26 | + /** | |
27 | + * 当前余额 | |
28 | + */ | |
29 | + amount?:number; | |
30 | + /** | |
31 | + * 待到账金额 | |
32 | + */ | |
33 | + awaitAmount?:number; | |
34 | + /** | |
35 | + * 低余额推待办 | |
36 | + */ | |
37 | + minAmount?:number; | |
38 | + /** | |
39 | + * 推待办角色编码 | |
40 | + */ | |
41 | + roleCode?:string; | |
42 | + /** | |
43 | + * 推待办角色名称 | |
44 | + */ | |
45 | + roleName?:string; | |
46 | + /** | |
47 | + * 发单账号 | |
48 | + */ | |
49 | + account?:string; | |
50 | + /** | |
51 | + * 集团id | |
52 | + */ | |
53 | + groupId?:number; | |
54 | + /** | |
55 | + * 创建时间 | |
56 | + */ | |
57 | + time?:string; | |
58 | +} | |
59 | +interface Params{ | |
60 | + current?:number, | |
61 | + pageSize?:number | |
62 | +} | |
63 | +export interface saveParams{ | |
64 | + //财务账户信息id | |
65 | + id?:number; | |
66 | + /** | |
67 | + * 往来单位id | |
68 | + */ | |
69 | + supplierId?:number; | |
70 | + /** | |
71 | + * 往来单位名称 | |
72 | + */ | |
73 | + supplierName?:string; | |
74 | + /** | |
75 | + * 签约门店id | |
76 | + */ | |
77 | + shopId?:number; | |
78 | + /** | |
79 | + * 低余额推待办 | |
80 | + */ | |
81 | + minAmount?:number; | |
82 | + /** | |
83 | + * 推待办角色编码 | |
84 | + */ | |
85 | + roleCode?:string; | |
86 | + /** | |
87 | + * 推待办角色名称 | |
88 | + */ | |
89 | + roleName?:string; | |
90 | + /** | |
91 | + * 发单账号 | |
92 | + */ | |
93 | + // @NotBlank(message = "发单账号不能为空") | |
94 | + account?:string; | |
95 | +} | |
96 | +interface queryList { | |
97 | + current?:number | |
98 | + pageSize?:number | |
99 | + sysId?:number | |
100 | +} | |
101 | +// 查询财务账户信息列表 | |
102 | +export function getList(params: Params):http.PromisePageResp<ListVO> { | |
103 | + return request.get(`${PMS_HOST}/erp/finance/account/get/page`, {params}); | |
104 | +} | |
105 | +// 新增/编辑API | |
106 | +export function saveApi(params: saveParams) { | |
107 | + return request.post(`${PMS_HOST}/erp/finance/account/exit`, {...params}); | |
108 | +} | |
109 | +// 推待办角色列表 | |
110 | +export function roleListApi(params: queryList): http.PromisePageResp<Role.Info> { | |
111 | + return request.get(`${HOST}/role/list`, { params }); | |
112 | +} | |
113 | +// 删除API | |
114 | +export function deleteApi(params: {id: number}) { | |
115 | + return request.post(`${PMS_HOST}/erp/finance/account/delete`, {...params}); | |
116 | +} | |
0 | 117 | \ No newline at end of file | ... | ... |
src/pages/pms/transfer/HuolalaSetting/components/AddModal.tsx
0 → 100644
1 | +import React, { useEffect, useState } from 'react'; | |
2 | +import {Form, Button, InputNumber, Modal, message, Input } from 'antd'; | |
3 | +import PmsSelect from '@/pages/pms/comonents/PmsSelect'; | |
4 | +import { ListVO, saveApi, roleListApi } from '../api'; | |
5 | +import { getPageListApi, Item as item } from "@/pages/pms/partPlan/PlanSupplier/api"; | |
6 | +import {getShopApi} from '@/pages/pms/storage/partShop/api'; | |
7 | +import usePagination from '@/hooks/usePagination'; | |
8 | +import useInitail from '@/hooks/useInitail'; | |
9 | + | |
10 | +const Item = Form.Item; | |
11 | + | |
12 | +interface Props{ | |
13 | + visible?:boolean, | |
14 | + onCancel: Function, | |
15 | + item?:ListVO | |
16 | +} | |
17 | + | |
18 | +export default function Index(props:Props) { | |
19 | + const { list: suppliers } = usePagination<item>(getPageListApi, { supplierType: 40, pageSize: 500 }); | |
20 | + const { data: shops } = useInitail<PmsStoragePartShop.Option[], {}>(getShopApi, [], {}); | |
21 | + const { list } = usePagination<Role.Info>(roleListApi, { pageSize: 500, sysId: 229 }); | |
22 | + const [loading, setLoading] = useState(false); | |
23 | + const { visible, onCancel, item } = props; | |
24 | + const [form] = Form.useForm(); | |
25 | + | |
26 | + useEffect(() => { | |
27 | + if (visible && item?.id) { | |
28 | + form.setFieldsValue({ | |
29 | + supplierId: item.supplierId, | |
30 | + shopId: item.shopId, | |
31 | + minAmount: item.minAmount, | |
32 | + roleCode: item.roleCode, | |
33 | + account: item.account | |
34 | + }); | |
35 | + } | |
36 | + if (!visible) { | |
37 | + form.resetFields(); | |
38 | + } | |
39 | + }, [visible]); | |
40 | + | |
41 | + const onSave = () => { | |
42 | + form.validateFields().then(files => { | |
43 | + const params = { | |
44 | + id: item?.id, | |
45 | + supplierId: files.supplierId, | |
46 | + supplierName: suppliers.find(i => i.supplierId == files.supplierId)?.supplierName, | |
47 | + shopId: files.shopId, | |
48 | + minAmount: files.minAmount, | |
49 | + roleCode: files.roleCode, | |
50 | + roleName: list.find(i => i.roleCode == files.roleCode)?.roleName, | |
51 | + account: files.account | |
52 | + }; | |
53 | + setLoading(true); | |
54 | + saveApi(params).then(res => { | |
55 | + message.success("保存成功"); | |
56 | + setLoading(false); | |
57 | + onCancel(); | |
58 | + }).catch(e => { | |
59 | + message.error(e.message); | |
60 | + setLoading(false); | |
61 | + }); | |
62 | + }); | |
63 | + }; | |
64 | + | |
65 | + return ( | |
66 | + <Modal | |
67 | + title={item?.id ? "编辑货拉拉账户配置" : "新增货拉拉账户配置"} | |
68 | + visible={visible} | |
69 | + maskClosable={false} | |
70 | + onCancel={() => onCancel()} | |
71 | + footer={[ | |
72 | + <Button loading={loading} onClick={() => onCancel()}>取消</Button>, | |
73 | + <Button loading={loading} type="primary" htmlType="submit" onClick={() => onSave()}>保存</Button> | |
74 | + ]} | |
75 | + > | |
76 | + <Form | |
77 | + form={form} | |
78 | + labelCol={{span: 5}} | |
79 | + wrapperCol={{span: 12}} | |
80 | + > | |
81 | + <Item label="往来单位" name="supplierId" rules={[{required: true, message: "请选择往来单位"}]}> | |
82 | + <PmsSelect | |
83 | + placeholder="选择往来单位" | |
84 | + options={suppliers.filter((item, index, self) => { | |
85 | + return self.findIndex(el => el.supplierId == item.supplierId) === index; | |
86 | + }).map((item) => ({ value: item.supplierId, label: item.supplierName }))} | |
87 | + /> | |
88 | + </Item> | |
89 | + <Item label="签约门店" name="shopId" rules={[{ required: true, message: "请选择签约门店" }]}> | |
90 | + <PmsSelect | |
91 | + placeholder="选择签约门店" | |
92 | + options={shops.map((item: PmsStoragePartShop.Option) => ({ value: item.id, label: item.name }))} | |
93 | + /> | |
94 | + </Item> | |
95 | + <Item label="推待办余额" name="minAmount" rules={[{ required: true, message: "请填写金额" }]}> | |
96 | + <InputNumber style={{width: '100%'}} addonAfter="元" /> | |
97 | + </Item> | |
98 | + <Item label="推待办角色" name="roleCode" rules={[{ required: true, message: "请选择推办角色" }]}> | |
99 | + <PmsSelect | |
100 | + placeholder="选择待办角色" | |
101 | + options={list.map(item => ({value: item.roleCode, label: item.roleName}))} | |
102 | + /> | |
103 | + </Item> | |
104 | + <Item label="发单账号" name="account" rules={[{ required: true, message: "请填写发单账号" }]}> | |
105 | + <Input /> | |
106 | + </Item> | |
107 | + </Form> | |
108 | + </Modal> | |
109 | + ); | |
110 | +} | ... | ... |
src/pages/pms/transfer/HuolalaSetting/components/ChargeModal.tsx
0 → 100644
1 | +import React from 'react'; | |
2 | +import { Form, Button, InputNumber, Modal } from 'antd'; | |
3 | +import PmsSelect from '@/pages/pms/comonents/PmsSelect'; | |
4 | + | |
5 | +const Item = Form.Item; | |
6 | + | |
7 | +interface Props { | |
8 | + visible?: boolean, | |
9 | + onCancel: Function, | |
10 | + item?: any | |
11 | +} | |
12 | +export default function Index(props: Props) { | |
13 | + const { visible, onCancel, item } = props; | |
14 | + const [form] = Form.useForm(); | |
15 | + | |
16 | + const onSave = () => { | |
17 | + const params = {id: item.id}; | |
18 | + console.log(params); | |
19 | + }; | |
20 | + | |
21 | + return ( | |
22 | + <Modal | |
23 | + title="预付款充值申请" | |
24 | + visible={visible} | |
25 | + maskClosable={false} | |
26 | + onCancel={() => onCancel()} | |
27 | + footer={[ | |
28 | + <Button onClick={() => onCancel()}>取消</Button>, | |
29 | + <Button type="primary" htmlType="submit" onClick={() => onSave()}>提交申请</Button> | |
30 | + ]} | |
31 | + > | |
32 | + <Form | |
33 | + form={form} | |
34 | + labelCol={{ span: 7 }} | |
35 | + wrapperCol={{ span: 12 }} | |
36 | + > | |
37 | + <Item label="往来单位" name="supplierName" rules={[{ required: true, message: "请选择往来单位" }]}> | |
38 | + <PmsSelect /> | |
39 | + </Item> | |
40 | + <Item label="往来单位类型" name="shopName" rules={[{ required: true, message: "请选择签约门店" }]}> | |
41 | + <PmsSelect /> | |
42 | + </Item> | |
43 | + <Item label="账户余额" name="amount" rules={[{ required: true, message: "请填写金额" }]}> | |
44 | + <InputNumber style={{ width: '100%' }} addonAfter="元" disabled /> | |
45 | + </Item> | |
46 | + <Item label="充值金额" name="awaitAmount" rules={[{ required: true, message: "请填写金额" }]}> | |
47 | + <InputNumber style={{ width: '100%' }} addonAfter="元" /> | |
48 | + </Item> | |
49 | + <Item label="账户到账金额" name="awaitAmount" rules={[{ required: true, message: "请填写金额" }]}> | |
50 | + <InputNumber style={{ width: '100%' }} addonAfter="元" disabled /> | |
51 | + </Item> | |
52 | + <Item label="结算门店" name="shopName" rules={[{ required: true, message: "请选择推办角色" }]}> | |
53 | + <PmsSelect /> | |
54 | + </Item> | |
55 | + <Item label="发票金额要求比例" name="role" rules={[{ required: true, message: "请选择推办角色" }]}> | |
56 | + <PmsSelect /> | |
57 | + </Item> | |
58 | + <Item label="结算方式" name="role" rules={[{ required: true, message: "请选择推办角色" }]}> | |
59 | + <PmsSelect /> | |
60 | + </Item> | |
61 | + <Item label="附件" name="files"> | |
62 | + <PmsSelect /> | |
63 | + </Item> | |
64 | + </Form> | |
65 | + </Modal> | |
66 | + ); | |
67 | +} | ... | ... |
src/pages/pms/transfer/HuolalaSetting/index.tsx
0 → 100644
1 | +import React, { useState } from 'react'; | |
2 | +import { Card, Button, Table, Divider, Popconfirm, message } from 'antd'; | |
3 | +import { PageHeaderWrapper } from '@ant-design/pro-layout'; | |
4 | +import AddModal from './components/AddModal'; | |
5 | +import ChargeModal from './components/ChargeModal'; | |
6 | +import {getList, ListVO, deleteApi} from './api'; | |
7 | +import usePagination from '@/hooks/usePagination'; | |
8 | + | |
9 | +const Column = Table.Column; | |
10 | +export default function Index() { | |
11 | + const {list, loading, paginationConfig, setParams} = usePagination<ListVO>(getList, [], {}); | |
12 | + const [current, setCurrent] = useState<{ visible: boolean, item: ListVO }>({visible: false, item: {}}); | |
13 | + const [chargeInfo, setChargeInfo] = useState<{ visible: boolean, item: ListVO }>({visible: false, item: {}}); | |
14 | + const data = [{id: 1, supplierName: "货拉拉", shopName: "不过承诺书可能单纯迪士尼才", amount: 3000, minAmount: 1500, roleName: "计划制定员", account: "15725173,16638183"}]; | |
15 | + return ( | |
16 | + <PageHeaderWrapper title="货拉拉账户配置"> | |
17 | + <Card | |
18 | + extra={<Button type="primary" onClick={() => setCurrent({visible: true, item: {}})}>新增</Button>} | |
19 | + > | |
20 | + <Table dataSource={data} rowKey="id" loading={loading} pagination={paginationConfig}> | |
21 | + <Column title="往来单位" dataIndex="supplierName" /> | |
22 | + <Column title="签约门店" dataIndex="shopName" /> | |
23 | + <Column title="当前余额(元)" dataIndex="amount" /> | |
24 | + <Column title="低余额推待办(元)" dataIndex="minAmount" /> | |
25 | + <Column title="推待办角色" dataIndex="roleName" /> | |
26 | + <Column title="发单账号" dataIndex="account" /> | |
27 | + <Column | |
28 | + title="操作" | |
29 | + render={r => ( | |
30 | + <div style={{display: 'flex', flexWrap: 'wrap', alignItems: 'center'}}> | |
31 | + <a onClick={() => setChargeInfo({visible: true, item: r})}>预付款充值申请</a> | |
32 | + <Divider type="vertical" /> | |
33 | + <a onClick={() => setCurrent({visible: true, item: r})}>编辑</a> | |
34 | + <Divider type="vertical" /> | |
35 | + <Popconfirm | |
36 | + title="确认删除" | |
37 | + onConfirm={() => { | |
38 | + deleteApi({id: r.id}).then(res => { | |
39 | + message.success('操作成功'); | |
40 | + setParams({}, true); | |
41 | + }).catch(e => message.error(e.message)); | |
42 | + }} | |
43 | + > | |
44 | + <a>删除</a> | |
45 | + </Popconfirm> | |
46 | + </div> | |
47 | + )} | |
48 | + /> | |
49 | + </Table> | |
50 | + <AddModal visible={current.visible} item={current.item} onCancel={() => setCurrent({ visible: false, item: {} })} /> | |
51 | + <ChargeModal visible={chargeInfo.visible} item={chargeInfo.item} onCancel={() => setChargeInfo({ visible: false, item: {} })} /> | |
52 | + </Card> | |
53 | + </PageHeaderWrapper> | |
54 | + ); | |
55 | +} | ... | ... |
src/pages/pms/transfer/transferPool/comonents/Filiter.tsx
... | ... | @@ -26,9 +26,9 @@ export default function Index() { |
26 | 26 | <Input.Search |
27 | 27 | allowClear |
28 | 28 | enterButton |
29 | - placeholder="输入关键词进行搜索" | |
29 | + placeholder="输入关键词搜索" | |
30 | 30 | onSearch={(value, event) => setParams({keywords: value}, true)} |
31 | - style={{ width: 220, marginRight: 10 }} | |
31 | + style={{ width: 200, marginRight: 10, marginBottom: 10 }} | |
32 | 32 | /> |
33 | 33 | <PmsSelect |
34 | 34 | style={{width: 200, marginRight: 10}} | ... | ... |
src/pages/pms/transfer/transferPool/comonents/PartTable.tsx
... | ... | @@ -22,7 +22,7 @@ export default function Index() { |
22 | 22 | > |
23 | 23 | <Column title="配件名称" dataIndex="name" /> |
24 | 24 | <Column title="配件编码" dataIndex="code" /> |
25 | - <Column title="动态库销比" dataIndex="ratio" /> | |
25 | + <Column title="动态库销比" dataIndex="ratio" render={t => (t ? t.toFixed(2) : '--')} /> | |
26 | 26 | <Column title="调出池配件数量" dataIndex="transferCnt" /> |
27 | 27 | <Column title="调出库房分析" render={(r:PoolItem) => <a onClick={() => analysis(r, 6)}>查看</a>} /> |
28 | 28 | <Column title="调入库房分析" render={(r:PoolItem) => <a onClick={() => analysis(r, 7)}>查看</a>} /> | ... | ... |
src/pages/pms/transfer/transferPool/comonents/StorageTable.tsx
... | ... | @@ -23,7 +23,7 @@ export default function Index(props: {out?:boolean}) { |
23 | 23 | scroll={{y: 500}} |
24 | 24 | > |
25 | 25 | <Column title={out ? "调出库房" : "调入库房"} dataIndex="name" /> |
26 | - <Column title="动态库销比" dataIndex="ratio" /> | |
26 | + <Column title="动态库销比" dataIndex="ratio" render={t => (t ? t.toFixed(2) : '--')} /> | |
27 | 27 | <Column title="调出池配件品种" dataIndex="transferKind" /> |
28 | 28 | <Column title="调出池配件数量" dataIndex="transferCnt" /> |
29 | 29 | {out ? | ... | ... |
src/pages/pms/transfer/transferPool/comonents/VehicleTable.tsx
... | ... | @@ -22,7 +22,7 @@ export default function Index() { |
22 | 22 | scroll={{y: 500}} |
23 | 23 | > |
24 | 24 | <Column title="车系" dataIndex="name" /> |
25 | - <Column title="动态库销比" dataIndex="ratio" /> | |
25 | + <Column title="动态库销比" dataIndex="ratio" render={t => (t ? t.toFixed(2) : '--')} /> | |
26 | 26 | <Column title="调出池配件品种" dataIndex="transferKind" /> |
27 | 27 | <Column title="调出池配件数量" dataIndex="transferCnt" /> |
28 | 28 | <Column title="调出库房分析" render={(r) => <a onClick={() => analysis(r, 6)}>查看</a>} /> | ... | ... |
src/pages/vms/OperationAdministration/components/PlayBackPicker/index.tsx
... | ... | @@ -59,6 +59,9 @@ function PlayBackPicker(props: any) { |
59 | 59 | const { _d: checkDate, _i: nowDate } = date; |
60 | 60 | const checkYear = moment(checkDate).format('YYYY-MM-DD'); |
61 | 61 | const nowYear = moment(nowDate).format('YYYY-MM-DD'); |
62 | + const checkHours = moment(checkDate).format('HH'); | |
63 | + const nowHours = moment(nowDate).format('HH'); | |
64 | + | |
62 | 65 | // if (type === 'start') { |
63 | 66 | // const hour = moment(date).hour(); |
64 | 67 | // const minute = moment(date).minute(); |
... | ... | @@ -83,24 +86,30 @@ function PlayBackPicker(props: any) { |
83 | 86 | // }, |
84 | 87 | // }; |
85 | 88 | // } |
86 | - if (checkYear === nowYear) { | |
87 | - const nowHour = moment(nowDate).hour(); | |
88 | - const nowMinute = moment(nowDate).minute(); | |
89 | - const nowSecond = moment(nowDate).second(); | |
90 | - return { | |
91 | - disabledHours: () => { | |
92 | - const disabledArr = timeArr(24).splice(nowHour, timeArr(24).length - nowHour); | |
93 | - return disabledArr; | |
94 | - }, | |
95 | - disabledMinutes: () => { | |
96 | - const disabledArr = timeArr(60).splice(nowMinute, timeArr(60).length - nowMinute); | |
97 | - return disabledArr; | |
98 | - }, | |
99 | - disabledSeconds: () => { | |
100 | - const disabledArr = timeArr(60).splice(nowSecond, timeArr(60).length - nowSecond); | |
101 | - return disabledArr; | |
102 | - }, | |
103 | - }; | |
89 | + | |
90 | + const nowHour = moment(nowDate).hour(); | |
91 | + const nowMinute = moment(nowDate).minute(); | |
92 | + const nowSecond = moment(nowDate).second(); | |
93 | + const resultLimit = { | |
94 | + // type: 'end', | |
95 | + disabledHours: () => { | |
96 | + const disabledArr = timeArr(24).splice(nowHour + 1, timeArr(24).length - nowHour); | |
97 | + return disabledArr; | |
98 | + }, | |
99 | + disabledMinutes: () => { | |
100 | + const disabledArr = timeArr(60).splice(nowMinute, timeArr(60).length - nowMinute); | |
101 | + return disabledArr; | |
102 | + }, | |
103 | + disabledSeconds: () => { | |
104 | + const disabledArr = timeArr(60).splice(nowSecond, timeArr(60).length - nowSecond); | |
105 | + return disabledArr; | |
106 | + }, | |
107 | + }; | |
108 | + if (checkYear === nowYear && checkHours !== nowHours) { | |
109 | + return { disabledHours: resultLimit.disabledHours }; | |
110 | + } | |
111 | + if (checkYear === nowYear && checkHours === nowHours) { | |
112 | + return resultLimit; | |
104 | 113 | } |
105 | 114 | }; |
106 | 115 | /** |
... | ... | @@ -135,6 +144,7 @@ function PlayBackPicker(props: any) { |
135 | 144 | }} |
136 | 145 | disabledDate={disabledDate} |
137 | 146 | disabledTime={disabledTracksTime} |
147 | + // max={disabledTracksTime} | |
138 | 148 | onCalendarChange={ |
139 | 149 | (value: any) => { |
140 | 150 | setDateFlag(value); | ... | ... |