Commit a73d2b93bef25ef4adbde7d3d9d7fc220d17daa5
Merge remote-tracking branch 'origin/pms'
Showing
6 changed files
with
158 additions
and
20 deletions
src/pages/pms/partPlan/PlanManage/subpages/Apply/components/PartModal.tsx
@@ -13,12 +13,15 @@ interface Props { | @@ -13,12 +13,15 @@ interface Props { | ||
13 | parts: any[] | 13 | parts: any[] |
14 | onOk: (parts: any[]) => any | 14 | onOk: (parts: any[]) => any |
15 | setParams: Function | 15 | setParams: Function |
16 | + _supplierId: number | undefined | ||
16 | } | 17 | } |
17 | const {Option} = Select; | 18 | const {Option} = Select; |
18 | -export default function Index({ onCancel, visible, parts=[], onOk, setParams }: Props) { | 19 | +export default function Index({ onCancel, visible, parts = [], onOk, setParams, _supplierId }: Props) { |
19 | const [selectedParts, setSelectedParts] = useState<any[]>([]); | 20 | const [selectedParts, setSelectedParts] = useState<any[]>([]); |
20 | const [dfParam, setDfParam] = useState<any>({keywords: ''}); | 21 | const [dfParam, setDfParam] = useState<any>({keywords: ''}); |
21 | const [partList, setPartList] = useState(parts); | 22 | const [partList, setPartList] = useState(parts); |
23 | + const [info, setInfo] = useState<{open: boolean, partName: string, poolId?: number}>({open: false, partName: '', poolId: undefined}); | ||
24 | + const [partArr, setPartArr] = useState<any[]>([]); | ||
22 | 25 | ||
23 | const shopNames = useMemo(() => { | 26 | const shopNames = useMemo(() => { |
24 | return Array.from(new Set(parts.map(it => it.shopName || '').filter(it => !!it))); | 27 | return Array.from(new Set(parts.map(it => it.shopName || '').filter(it => !!it))); |
@@ -37,6 +40,7 @@ export default function Index({ onCancel, visible, parts=[], onOk, setParams }: | @@ -37,6 +40,7 @@ export default function Index({ onCancel, visible, parts=[], onOk, setParams }: | ||
37 | useEffect(() => { | 40 | useEffect(() => { |
38 | if (!visible) { | 41 | if (!visible) { |
39 | setSelectedParts([]); | 42 | setSelectedParts([]); |
43 | + setPartArr([]); | ||
40 | setPartList(parts); | 44 | setPartList(parts); |
41 | } | 45 | } |
42 | }, [visible, parts]); | 46 | }, [visible, parts]); |
@@ -46,6 +50,14 @@ export default function Index({ onCancel, visible, parts=[], onOk, setParams }: | @@ -46,6 +50,14 @@ export default function Index({ onCancel, visible, parts=[], onOk, setParams }: | ||
46 | message.error("请选择配件"); | 50 | message.error("请选择配件"); |
47 | return; | 51 | return; |
48 | } | 52 | } |
53 | + if (partArr.some(it => !!it.supplierId)) { | ||
54 | + for (const item of partArr) { | ||
55 | + if (!!item.supplierId && item.supplierId != _supplierId) { | ||
56 | + setInfo({ open: true, partName: item.partName, poolId: item.poolId }); | ||
57 | + return; | ||
58 | + } | ||
59 | + } | ||
60 | + } | ||
49 | onOk && onOk(selectedParts); | 61 | onOk && onOk(selectedParts); |
50 | onCancel && onCancel(); | 62 | onCancel && onCancel(); |
51 | } | 63 | } |
@@ -81,6 +93,7 @@ export default function Index({ onCancel, visible, parts=[], onOk, setParams }: | @@ -81,6 +93,7 @@ export default function Index({ onCancel, visible, parts=[], onOk, setParams }: | ||
81 | visible={visible} | 93 | visible={visible} |
82 | title="配件采购明细" | 94 | title="配件采购明细" |
83 | onCancel={onCancel} | 95 | onCancel={onCancel} |
96 | + maskClosable={false} | ||
84 | footer={[ | 97 | footer={[ |
85 | <Button key="cancel" onClick={onCancel}>取消</Button>, | 98 | <Button key="cancel" onClick={onCancel}>取消</Button>, |
86 | <Button | 99 | <Button |
@@ -195,6 +208,7 @@ export default function Index({ onCancel, visible, parts=[], onOk, setParams }: | @@ -195,6 +208,7 @@ export default function Index({ onCancel, visible, parts=[], onOk, setParams }: | ||
195 | } else if (index > -1) { | 208 | } else if (index > -1) { |
196 | newData.splice(index, 1); | 209 | newData.splice(index, 1); |
197 | } | 210 | } |
211 | + setPartArr(newData); | ||
198 | setSelectedParts([...newData]); | 212 | setSelectedParts([...newData]); |
199 | }, | 213 | }, |
200 | onSelectAll: (selected, selectedRows, changeRows) => { | 214 | onSelectAll: (selected, selectedRows, changeRows) => { |
@@ -206,6 +220,7 @@ export default function Index({ onCancel, visible, parts=[], onOk, setParams }: | @@ -206,6 +220,7 @@ export default function Index({ onCancel, visible, parts=[], onOk, setParams }: | ||
206 | } else { | 220 | } else { |
207 | newData = selectedParts.filter(row => !changedKeys.includes(`${row.poolId}`)); | 221 | newData = selectedParts.filter(row => !changedKeys.includes(`${row.poolId}`)); |
208 | } | 222 | } |
223 | + setPartArr(newData); | ||
209 | setSelectedParts(newData); | 224 | setSelectedParts(newData); |
210 | }, | 225 | }, |
211 | }} | 226 | }} |
@@ -220,6 +235,25 @@ export default function Index({ onCancel, visible, parts=[], onOk, setParams }: | @@ -220,6 +235,25 @@ export default function Index({ onCancel, visible, parts=[], onOk, setParams }: | ||
220 | <Column title="配件来源类型" dataIndex="typeName" /> | 235 | <Column title="配件来源类型" dataIndex="typeName" /> |
221 | <Column title="上次采购供应商" dataIndex="supplierName" /> | 236 | <Column title="上次采购供应商" dataIndex="supplierName" /> |
222 | </Table> | 237 | </Table> |
238 | + <Modal | ||
239 | + title="提示" | ||
240 | + open={info.open} | ||
241 | + maskClosable={false} | ||
242 | + closable={false} | ||
243 | + cancelText="重新选择" | ||
244 | + okText="继续" | ||
245 | + onCancel={() => { | ||
246 | + setPartArr(partArr.filter((i => i.poolId != info.poolId))); | ||
247 | + setSelectedParts(selectedParts.filter((i => i.poolId != info.poolId))); | ||
248 | + setInfo({open: false, partName: '', poolId: undefined}); | ||
249 | + }} | ||
250 | + onOk={() => { | ||
251 | + setPartArr(partArr.filter((i => i.poolId != info.poolId))); | ||
252 | + setInfo({ open: false, partName: '', poolId: undefined }); | ||
253 | + }} | ||
254 | + > | ||
255 | + <p style={{ color: 'red' }}><span style={{ color: '#333333', fontWeight: '500', marginRight: 3}}>【{info.partName}】</span>本次采购供应商和上次采购供应商不一致,是否继续?</p> | ||
256 | + </Modal> | ||
223 | </Modal> | 257 | </Modal> |
224 | ); | 258 | ); |
225 | } | 259 | } |
src/pages/pms/partPlan/PlanManage/subpages/Apply/index.tsx
@@ -41,6 +41,7 @@ export default function Index() { | @@ -41,6 +41,7 @@ export default function Index() { | ||
41 | const partList = flattenDeep(dealerList.map(it => (it.suppliers || []).map((su: any) => (su.storages || []).map((st: any) => (st.parts || []))))); | 41 | const partList = flattenDeep(dealerList.map(it => (it.suppliers || []).map((su: any) => (su.storages || []).map((st: any) => (st.parts || []))))); |
42 | const poolIds = partList.map((it: any) => it.poolId); | 42 | const poolIds = partList.map((it: any) => it.poolId); |
43 | const [info, setInfo] = useState<{ remark?: string, fids?: any }>(); | 43 | const [info, setInfo] = useState<{ remark?: string, fids?: any }>(); |
44 | + const [_supplierId, setSupplierId] = useState<number| undefined>(undefined); | ||
44 | 45 | ||
45 | useEffect(() => { | 46 | useEffect(() => { |
46 | if (planId?.planId) { | 47 | if (planId?.planId) { |
@@ -254,7 +255,7 @@ export default function Index() { | @@ -254,7 +255,7 @@ export default function Index() { | ||
254 | <div key={`supplier${supplier.supplierId}`} style={{ marginTop: 10, marginLeft: 40 }}> | 255 | <div key={`supplier${supplier.supplierId}`} style={{ marginTop: 10, marginLeft: 40 }}> |
255 | <div style={{ display: 'flex', flexDirection: 'row', alignItems: 'center'}}> | 256 | <div style={{ display: 'flex', flexDirection: 'row', alignItems: 'center'}}> |
256 | <div style={{ fontWeight: "bold" }}>{`供应商: ${supplier.supplierName || ''}`}</div> | 257 | <div style={{ fontWeight: "bold" }}>{`供应商: ${supplier.supplierName || ''}`}</div> |
257 | - <a style={{marginLeft: 20}} onClick={() => { setVisiblePart(true); setDealer({...dealer, ...supplier}); }}> | 258 | + <a style={{ marginLeft: 20 }} onClick={() => { setVisiblePart(true); setDealer({ ...dealer, ...supplier }); setSupplierId(supplier.supplierId); }}> |
258 | 添加采购配件 | 259 | 添加采购配件 |
259 | </a> | 260 | </a> |
260 | <Popconfirm | 261 | <Popconfirm |
@@ -322,6 +323,7 @@ export default function Index() { | @@ -322,6 +323,7 @@ export default function Index() { | ||
322 | parts={parts.filter(it => !poolIds.includes(it.poolId))} | 323 | parts={parts.filter(it => !poolIds.includes(it.poolId))} |
323 | onOk={onOk} | 324 | onOk={onOk} |
324 | setParams={setParams} | 325 | setParams={setParams} |
326 | + _supplierId={_supplierId} | ||
325 | /> | 327 | /> |
326 | <PartDetailModal | 328 | <PartDetailModal |
327 | visible={visiblePartDetail} | 329 | visible={visiblePartDetail} |
src/pages/pms/partPlan/PlanShipping/api.ts
@@ -36,10 +36,91 @@ export interface Item { | @@ -36,10 +36,91 @@ export interface Item { | ||
36 | otherRatio?: number; // 对方责任占比(几成) | 36 | otherRatio?: number; // 对方责任占比(几成) |
37 | againCnt?: number; // 补发数量 | 37 | againCnt?: number; // 补发数量 |
38 | } | 38 | } |
39 | +export interface Detail { | ||
40 | + brandId?:number | ||
41 | + // int64 | ||
42 | + // 品牌ID | ||
43 | + // 510 | ||
44 | + brandName?: | ||
45 | + string | ||
46 | + // 品牌名称 | ||
47 | +// opal.prohaska | ||
48 | +supplierId?:number | ||
49 | +// int64 | ||
50 | +// 供应商id | ||
51 | +// 950 | ||
52 | +partKind?:number | ||
53 | +// int32 | ||
54 | +// 配件种类 | ||
55 | +// 863 | ||
56 | +supplierName?: | ||
57 | +string | ||
58 | +// 供应商名称 | ||
59 | +// opal.prohaska | ||
60 | +shopId?:number | ||
61 | +// int64 | ||
62 | +// 服务站id | ||
63 | +// 705/ | ||
64 | +shopName?: | ||
65 | +string | ||
66 | +// 门店名称 | ||
67 | +// opal.prohaska | ||
68 | +storageId?:number | ||
69 | +// int64 | ||
70 | +// 库房id | ||
71 | +// 563 | ||
72 | +storageName?: | ||
73 | +string | ||
74 | +// 库房名称 | ||
75 | +// opal.prohaska | ||
76 | +groupId?:number | ||
77 | +// int64 | ||
78 | +// 集团ID | ||
79 | +// 33/ | ||
80 | +shippingNo?: | ||
81 | +string | ||
82 | +// 发运单号 | ||
83 | +// zt910g | ||
84 | +shippingDate?: | ||
85 | +string | ||
86 | +// 发运日期?; | ||
87 | +// 2023 - 05 - 23 | ||
88 | +totalAmount?: | ||
89 | +number | ||
90 | +// 总金额 | ||
91 | +// 904 | ||
92 | +payToken?: | ||
93 | +string | ||
94 | +// 待付token | ||
95 | +// wywo3x | ||
96 | +importUserName?: | ||
97 | +string | ||
98 | +// 导入人员 | ||
99 | +// opal.prohaska | ||
100 | +inStorageUserName?: | ||
101 | +string | ||
102 | +// 入库人员 | ||
103 | +// opal.prohaska | ||
104 | +status?:number | ||
105 | +// enum | ||
106 | + // 状态0待确认1待入库2已完成9作废(See: 配件发运单状态 | ||
107 | +// create at 2020-03 - 18) | ||
108 | + | ||
109 | +settleDealerId?:number | ||
110 | +// int64 | ||
111 | +// 结算商家/Id | ||
112 | +// 115 | ||
113 | +settleDealerName?: | ||
114 | +string | ||
115 | +// 结算商家名称 | ||
116 | + settleShopId?:number | ||
117 | + settleShopName?:string | ||
118 | + list?: DetailItem[] | ||
119 | +} | ||
39 | /** | 120 | /** |
40 | * 明细 | 121 | * 明细 |
41 | */ | 122 | */ |
42 | -export interface Detail { | 123 | +export interface DetailItem { |
43 | supplierName?: string; // 供应商名称 | 124 | supplierName?: string; // 供应商名称 |
44 | partId?: number; // 配件ID | 125 | partId?: number; // 配件ID |
45 | partName?: string; // 配件名称 | 126 | partName?: string; // 配件名称 |
@@ -68,8 +149,8 @@ export function getList(params?: Params): http.PromisePageResp<Item> { | @@ -68,8 +149,8 @@ export function getList(params?: Params): http.PromisePageResp<Item> { | ||
68 | /** | 149 | /** |
69 | * 查询明细 | 150 | * 查询明细 |
70 | */ | 151 | */ |
71 | -export function getDetail(shippingNo?: string): http.PromiseResp<Detail[]> { | ||
72 | - return request.get(`${PMS_HOST}/app/part/shipping/detail`, { params: {shippingNo} }); | 152 | +export function getDetail(shippingNo?: string): http.PromiseResp<Detail> { |
153 | + return request.get(`${PMS_HOST}/app/part/shipping/part/detail`, { params: {shippingNo} }); | ||
73 | } | 154 | } |
74 | 155 | ||
75 | /** | 156 | /** |
src/pages/pms/partPlan/PlanShipping/components/ConfirmDetailModal.tsx
1 | import React, { useState, useEffect } from 'react'; | 1 | import React, { useState, useEffect } from 'react'; |
2 | -import { Button, message, Modal, Table } from 'antd'; | ||
3 | -import { confirmApi, getDetail, Detail, Item } from "../api"; | 2 | +import { Button, message, Modal, Table, Descriptions } from 'antd'; |
3 | +import { confirmApi, getDetail, Detail, Item, DetailItem } from "../api"; | ||
4 | import useInitail from "@/hooks/useInitail"; | 4 | import useInitail from "@/hooks/useInitail"; |
5 | import _ from "lodash"; | 5 | import _ from "lodash"; |
6 | 6 | ||
@@ -16,7 +16,7 @@ const { Column } = Table; | @@ -16,7 +16,7 @@ const { Column } = Table; | ||
16 | export default function Index(props: Props) { | 16 | export default function Index(props: Props) { |
17 | const { visible, onCancel, fetchList, item, confirm } = props; | 17 | const { visible, onCancel, fetchList, item, confirm } = props; |
18 | const [delay, setDelay] = useState(true); | 18 | const [delay, setDelay] = useState(true); |
19 | - const { data, setParams, loading: aloading} = useInitail<Detail[], string | undefined>(getDetail, [], item.shippingNo, delay); | 19 | + const { data, setParams, loading: aloading} = useInitail<Detail, string | undefined>(getDetail, {}, item.shippingNo, delay); |
20 | const [loading, setLoading] = useState(false); | 20 | const [loading, setLoading] = useState(false); |
21 | 21 | ||
22 | useEffect(() => { | 22 | useEffect(() => { |
@@ -54,13 +54,19 @@ export default function Index(props: Props) { | @@ -54,13 +54,19 @@ export default function Index(props: Props) { | ||
54 | <Button key="2" type="primary" loading={loading} disabled={loading} onClick={() => submit(true)}>确认</Button> | 54 | <Button key="2" type="primary" loading={loading} disabled={loading} onClick={() => submit(true)}>确认</Button> |
55 | ] : [<Button key="1" loading={loading} onClick={() => onCancel()}>取消</Button>]} | 55 | ] : [<Button key="1" loading={loading} onClick={() => onCancel()}>取消</Button>]} |
56 | > | 56 | > |
57 | - <Table loading={aloading} rowKey={(v: Detail) => `${v.partId}`} scroll={{y: 500, x: 1200}} dataSource={data || []} pagination={false}> | 57 | + <Descriptions column={3}> |
58 | + <Descriptions.Item label="品牌">{data.brandName}</Descriptions.Item> | ||
59 | + <Descriptions.Item label="供应商" span={2}>{data.supplierName}</Descriptions.Item> | ||
60 | + <Descriptions.Item label="库房">{data.storageName}</Descriptions.Item> | ||
61 | + <Descriptions.Item label="提报门店" span={2}>{data.settleShopName}</Descriptions.Item> | ||
62 | + </Descriptions> | ||
63 | + <Table loading={aloading} rowKey={(v: DetailItem) => `${v.partId}`} scroll={{y: 500, x: 1200}} dataSource={data.list || []} pagination={false}> | ||
58 | <Column title="配件名称" dataIndex="partName" /> | 64 | <Column title="配件名称" dataIndex="partName" /> |
59 | <Column title="配件编码" dataIndex="partCode" /> | 65 | <Column title="配件编码" dataIndex="partCode" /> |
60 | <Column title="配件件号" dataIndex="partNo" /> | 66 | <Column title="配件件号" dataIndex="partNo" /> |
61 | <Column title="发运数量" dataIndex="partCount" /> | 67 | <Column title="发运数量" dataIndex="partCount" /> |
62 | <Column title="采购单价" dataIndex="price" /> | 68 | <Column title="采购单价" dataIndex="price" /> |
63 | - <Column title="总金额(元)" dataIndex="totalAmount" render={(t: number, _:Detail) => ((_.price || 0) * (_.partCount || 0) || '--')} /> | 69 | + <Column title="总金额(元)" dataIndex="totalAmount" render={(t: number, _: DetailItem) => ((_.price || 0) * (_.partCount || 0) || '--')} /> |
64 | {!confirm && <Column title="入库数量" dataIndex="storageCnt" render={(t: number) => (t || '--')} />} | 70 | {!confirm && <Column title="入库数量" dataIndex="storageCnt" render={(t: number) => (t || '--')} />} |
65 | {!confirm && <Column title="遗漏数量" dataIndex="omitCnt" render={(t: number) => (t || '--')} />} | 71 | {!confirm && <Column title="遗漏数量" dataIndex="omitCnt" render={(t: number) => (t || '--')} />} |
66 | {!confirm && <Column title="破损数量" dataIndex="damagedCnt" render={(t: number) => (t || '--')} />} | 72 | {!confirm && <Column title="破损数量" dataIndex="damagedCnt" render={(t: number) => (t || '--')} />} |
src/pages/pms/partPlan/PlanShipping/components/UploadExcel.tsx
@@ -107,7 +107,7 @@ export default function UploadExcel({ getList, importVisible, setImportVisible, | @@ -107,7 +107,7 @@ export default function UploadExcel({ getList, importVisible, setImportVisible, | ||
107 | onChange={supplierId => setParam({ ...param, supplierId })} | 107 | onChange={supplierId => setParam({ ...param, supplierId })} |
108 | /> | 108 | /> |
109 | <SelectRow | 109 | <SelectRow |
110 | - title="库房" | 110 | + title="发运库房" |
111 | value={param.storageId} | 111 | value={param.storageId} |
112 | data={storages} | 112 | data={storages} |
113 | id="id" | 113 | id="id" |
@@ -115,7 +115,7 @@ export default function UploadExcel({ getList, importVisible, setImportVisible, | @@ -115,7 +115,7 @@ export default function UploadExcel({ getList, importVisible, setImportVisible, | ||
115 | onChange={storageId => setParam({ ...param, storageId })} | 115 | onChange={storageId => setParam({ ...param, storageId })} |
116 | /> | 116 | /> |
117 | <SelectRow | 117 | <SelectRow |
118 | - title="结算门店" | 118 | + title="提报门店" |
119 | value={param.settleShopId} | 119 | value={param.settleShopId} |
120 | data={shops} | 120 | data={shops} |
121 | id="id" | 121 | id="id" |
src/pages/pms/partPlan/PlanShipping/index.tsx
1 | import React, { useState } from "react"; | 1 | import React, { useState } from "react"; |
2 | -import { Card, ConfigProvider, DatePicker, Divider, Table } from 'antd'; | 2 | +import { Card, ConfigProvider, DatePicker, Divider, Table, Dropdown, Menu } from 'antd'; |
3 | +import { DownOutlined } from '@ant-design/icons'; | ||
3 | import { PageHeaderWrapper } from '@ant-design/pro-layout'; | 4 | import { PageHeaderWrapper } from '@ant-design/pro-layout'; |
4 | import usePagination from "@/hooks/usePagination"; | 5 | import usePagination from "@/hooks/usePagination"; |
5 | import DetailModal from './components/ConfirmDetailModal'; | 6 | import DetailModal from './components/ConfirmDetailModal'; |
@@ -23,6 +24,23 @@ export default function PartPriceCoefficient() { | @@ -23,6 +24,23 @@ export default function PartPriceCoefficient() { | ||
23 | const [item, setItem] = useState<Item>({}); | 24 | const [item, setItem] = useState<Item>({}); |
24 | const { data: shops } = useInitial<PmsStoragePartShop.Option[], {}>(api.getShopApi, [], {}); | 25 | const { data: shops } = useInitial<PmsStoragePartShop.Option[], {}>(api.getShopApi, [], {}); |
25 | 26 | ||
27 | + const menu = ( | ||
28 | + <Menu | ||
29 | + items={ | ||
30 | + [ | ||
31 | + { | ||
32 | + label: <a href="/api/pms/erp/part/template/shipping">通用模板</a>, | ||
33 | + key: '0', | ||
34 | + }, | ||
35 | + { | ||
36 | + label: <a href="/api/pms/erp/part/template/shipping/ca">长安模板</a>, | ||
37 | + key: '1', | ||
38 | + }, | ||
39 | + ] | ||
40 | + } | ||
41 | + /> | ||
42 | +); | ||
43 | + | ||
26 | return ( | 44 | return ( |
27 | <PageHeaderWrapper title="发运单"> | 45 | <PageHeaderWrapper title="发运单"> |
28 | <ConfigProvider locale={zhCN}> | 46 | <ConfigProvider locale={zhCN}> |
@@ -56,13 +74,10 @@ export default function PartPriceCoefficient() { | @@ -56,13 +74,10 @@ export default function PartPriceCoefficient() { | ||
56 | /> | 74 | /> |
57 | </div> | 75 | </div> |
58 | <div style={{ display: 'flex', flexDirection: 'row'}}> | 76 | <div style={{ display: 'flex', flexDirection: 'row'}}> |
59 | - <a | ||
60 | - href={`/api/pms/erp/part/template/shipping?t=${moment().valueOf()}`} | ||
61 | - style={{ marginRight: 20 }} | ||
62 | - > | ||
63 | - 下载模板 | ||
64 | - </a> | ||
65 | - <a onClick={() => { setImportVisible(true); }}>导入数据</a> | 77 | + <Dropdown overlay={menu} trigger={['click']} placement="bottomLeft" arrow overlayStyle={{width: 120}}> |
78 | + <a style={{display: 'flex', alignItems: 'center'}}>导入模板<DownOutlined style={{fontSize: 14, marginLeft: 3}} /></a> | ||
79 | + </Dropdown> | ||
80 | + <div style={{marginLeft: 20}}><a onClick={() => { setImportVisible(true); }}>导入数据</a></div> | ||
66 | </div> | 81 | </div> |
67 | </div> | 82 | </div> |
68 | <Table | 83 | <Table |