Commit 6e1e695ac49f8cb56d446b489c31f8af0dc34490
1 parent
3b916ae7
线索到店零售占比配置
Showing
8 changed files
with
205 additions
and
49 deletions
config/routers/order3.ts
... | ... | @@ -202,4 +202,8 @@ export default [ |
202 | 202 | path: "/order3/orderSetting/deliveryVideoConfig", |
203 | 203 | component: "./order3/OrderSetting/DeliveryVideoConfig", |
204 | 204 | }, |
205 | + { // 零售线索占比配置 | |
206 | + path: "/order3/retailTaskConfiguration", | |
207 | + component: "./order3/RetailTaskConfiguration", | |
208 | + }, | |
205 | 209 | ]; |
206 | 210 | \ No newline at end of file | ... | ... |
src/pages/order3/RetailTaskConfiguration/api.ts
... | ... | @@ -2,11 +2,27 @@ import { http } from '@/typing/http'; |
2 | 2 | import request from '@/utils/request'; |
3 | 3 | import { ORDER3 } from '@/utils/host'; |
4 | 4 | |
5 | +export interface List { | |
6 | + id?: number // id | |
7 | + retailRate?: number // 线索到店零售占比 | |
8 | + shopList?: ShopList[] // 门店列表 | |
9 | +} | |
10 | + | |
11 | +export interface ShopList { | |
12 | + shopId?: number // 门店id | |
13 | + shopName?: string // 门店名称 | |
14 | +} | |
5 | 15 | /** 获取列表 */ |
6 | -export function getRetailListApi(params?: any): http.PromiseResp<any> { | |
7 | - return request.get(`${ORDER3}/erp/sales/task/detail`, { params }); | |
16 | +export function getRetailListApi(): http.PromiseResp<any> { | |
17 | + return request.get(`${ORDER3}/erp/clue/deal/rate/config/list`); | |
8 | 18 | } |
9 | -// /*提交 */ | |
10 | -// export function save(id?: number) { | |
11 | -// return request.post(`${ORDER3}/erp/sales/task/submit`, { id }, { contentType: 'form-urlencoded' }); | |
12 | -// } | |
13 | 19 | \ No newline at end of file |
20 | + | |
21 | +/* 新增编辑线索到店零售占比配置*/ | |
22 | +export function saveRetailConfigApi(params?: List) { | |
23 | + return request.post(`${ORDER3}/erp/clue/deal/rate/config/save`, params); | |
24 | +} | |
25 | + | |
26 | +/* 删除线索到店零售占比配置*/ | |
27 | +export function fetchDeleteConfigApi(id?: number) { | |
28 | + return request.post(`${ORDER3}/erp/clue/deal/rate/config/delete`, { id }, { contentType: 'form-urlencoded' }); | |
29 | +} | |
14 | 30 | \ No newline at end of file | ... | ... |
src/pages/order3/RetailTaskConfiguration/components/EditMidal.tsx
... | ... | @@ -5,38 +5,63 @@ import { debounce } from 'lodash'; |
5 | 5 | import currency from 'currency.js'; |
6 | 6 | import useInitail from '@/hooks/useInitail'; |
7 | 7 | import { useStore } from '../index'; |
8 | +import { saveRetailConfigApi } from '../api'; | |
8 | 9 | |
9 | 10 | const Option = Select.Option; |
10 | 11 | |
11 | 12 | export default function Index() { |
12 | 13 | const [form] = Form.useForm(); |
13 | - const { visible, setVisible } = useStore(); | |
14 | + const { visible, setVisible, current, setCurrent, setLoading } = useStore(); | |
14 | 15 | const [confirm, setConfirm] = useState<boolean>(false); |
15 | 16 | |
16 | 17 | useEffect(() => { |
17 | - if (visible) { | |
18 | + if (visible && current.id) { | |
18 | 19 | handleSetValue(); |
19 | 20 | } |
20 | 21 | }, [visible]); |
21 | 22 | |
22 | 23 | function handleCancle() { |
23 | 24 | setVisible(false); |
25 | + setCurrent({}); | |
24 | 26 | } |
25 | 27 | |
26 | 28 | async function handleSubmit() { |
27 | 29 | const params = await form.validateFields(); |
28 | 30 | setConfirm(true); |
31 | + const _params = { | |
32 | + id: current.id, | |
33 | + retailRate: params.retailRate, | |
34 | + shopList: params.shopList?.map((v: any) => ({shopId: v.value, shopName: v.label})) | |
35 | + }; | |
36 | + saveRetailConfigApi(_params) | |
37 | + .then(res => { | |
38 | + message.success(res.result); | |
39 | + setVisible(false); | |
40 | + setConfirm(false); | |
41 | + setLoading(true); | |
42 | + setCurrent({}); | |
43 | + }) | |
44 | + .catch(e => { | |
45 | + message.error(e.message); | |
46 | + setConfirm(false); | |
47 | + }); | |
29 | 48 | } |
30 | 49 | |
31 | 50 | function handleSetValue() { |
32 | - form.setFieldsValue({}); | |
51 | + form.setFieldsValue({ | |
52 | + retailRate: current.retailRate, | |
53 | + shopList: current.shopList?.map(v => ({label: v.shopName, value: v.shopId})) | |
54 | + }); | |
33 | 55 | } |
34 | 56 | return ( |
35 | 57 | <Modal |
58 | + title={current.id ? "编辑" : "新增"} | |
36 | 59 | destroyOnClose |
37 | 60 | visible={visible} |
38 | 61 | maskClosable={false} |
39 | 62 | onCancel={handleCancle} |
63 | + afterClose={() => form.setFieldsValue({shopList: [], retailRate: undefined})} | |
64 | + style={{minWidth: "500px"}} | |
40 | 65 | footer={[ |
41 | 66 | <Button key="cancel" onClick={handleCancle} style={{marginLeft: 10}}>取消</Button>, |
42 | 67 | <Button key="submit" onClick={handleSubmit} type="primary" htmlType="submit" loading={confirm}>确认</Button> |
... | ... | @@ -55,29 +80,10 @@ export default function Index() { |
55 | 80 | > |
56 | 81 | <ShopSelectNew placeholder="请选择门店" multiple /> |
57 | 82 | </Form.Item> |
58 | - {/* <Form.Item | |
59 | - label="适用门店" | |
60 | - name="shopList" | |
61 | - rules={[{ required: true, message: "请选择门店" }]} | |
62 | - > | |
63 | - <Select | |
64 | - optionFilterProp="children" | |
65 | - mode="multiple" | |
66 | - labelInValue | |
67 | - allowClear | |
68 | - style={{ width: "100%" }} | |
69 | - placeholder="请选择门店" | |
70 | - > | |
71 | - {shop && shop.map((shop: any) => ( | |
72 | - <Option value={shop.id} key={shop.id}> | |
73 | - {shop.shopName} | |
74 | - </Option> | |
75 | - ))} | |
76 | - </Select> | |
77 | - </Form.Item> */} | |
83 | + | |
78 | 84 | <Form.Item |
79 | 85 | label="线索到店零售台数占比" |
80 | - name="shopList" | |
86 | + name="retailRate" | |
81 | 87 | rules={[{ required: true, message: "请选择门店" }]} |
82 | 88 | > |
83 | 89 | <InputNumber | ... | ... |
src/pages/order3/RetailTaskConfiguration/components/List.tsx
1 | 1 | import React, { useState } from "react"; |
2 | -import { message, Popconfirm, Table, Tooltip } from "antd"; | |
2 | +import { message, Popconfirm, Table, Space } from "antd"; | |
3 | 3 | import moment from "moment"; |
4 | +import { useStore } from '../index'; | |
5 | +import { fetchDeleteConfigApi, List } from '../api'; | |
6 | +import { isNil } from 'lodash'; | |
4 | 7 | |
5 | 8 | const Column = Table.Column; |
6 | 9 | |
7 | -export default function List() { | |
10 | +export default function TableList() { | |
11 | + const {data, loading, setLoading, setVisible, setCurrent, setStatusData } = useStore(); | |
12 | + | |
13 | + function handleDelete(id?: number) { | |
14 | + fetchDeleteConfigApi(id) | |
15 | + .then(res => { | |
16 | + message.success(res.result); | |
17 | + setLoading(true); | |
18 | + }) | |
19 | + .catch(e => { | |
20 | + message.error(e.message); | |
21 | + }); | |
22 | + } | |
23 | + | |
24 | + function handleEdit(value?: List) { | |
25 | + setCurrent(value || {}); | |
26 | + setVisible(true); | |
27 | + } | |
28 | + | |
29 | + function handleLookShop(value: any = {}) { | |
30 | + setStatusData({visible: true, data: value.shopList || []}); | |
31 | + } | |
32 | + | |
8 | 33 | return ( |
9 | 34 | <div> |
10 | 35 | <Table |
11 | - // dataSource={list} | |
12 | - // pagination={paginationConfig} | |
13 | - // loading={loading} | |
36 | + dataSource={data} | |
37 | + pagination={false} | |
38 | + loading={loading} | |
14 | 39 | rowKey="id" |
15 | 40 | > |
16 | 41 | <Column |
17 | 42 | title="适用门店" |
18 | 43 | align="left" |
19 | - dataIndex="brandName" | |
44 | + dataIndex="shopList" | |
45 | + render={(_text, record) => <span onClick={() => handleLookShop(record)} style={{color: "#4189FD"}}>查看</span>} | |
20 | 46 | /> |
21 | 47 | <Column |
22 | 48 | title="线索到店零售台数占比" |
23 | 49 | align="left" |
24 | - dataIndex="brandName" | |
50 | + dataIndex="retailRate" | |
51 | + render={(_text, record: any) => <span>{isNil(record?.retailRate) ? "--" : `${record.retailRate}%`}</span>} | |
25 | 52 | /> |
26 | 53 | <Column |
27 | 54 | title="操作" |
28 | 55 | align="left" |
29 | 56 | render={(_text, record: any) => { |
30 | 57 | return ( |
31 | - <> | |
32 | - <a style={{ display: "block" }}>编辑</a> | |
58 | + <Space> | |
59 | + <a onClick={() => handleEdit(record)} style={{ display: "block", color: "#4189FD" }}>编辑</a> | |
33 | 60 | <Popconfirm |
34 | 61 | title="是否删除?" |
35 | 62 | okText="确定" |
36 | 63 | cancelText="取消" |
64 | + onConfirm={() => handleDelete(record.id)} | |
37 | 65 | > |
38 | - <a>删除</a> | |
66 | + <a style={{color: "#EC3F2F"}}>删除</a> | |
39 | 67 | </Popconfirm> |
40 | - </> | |
68 | + </Space> | |
41 | 69 | ); |
42 | 70 | }} |
43 | 71 | /> | ... | ... |
src/pages/order3/RetailTaskConfiguration/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 | +import styles from '../index.less'; | |
5 | + | |
6 | +export default function Index() { | |
7 | + const { statusData, setStatusData } = useStore(); | |
8 | + function handleCancel() { | |
9 | + setStatusData({visible: false, data: []}); | |
10 | + } | |
11 | + | |
12 | + return ( | |
13 | + <Modal | |
14 | + destroyOnClose | |
15 | + title={<div className={styles.lineWrap}><span className={styles.line} /><span className={styles.lineText}>适用门店</span></div>} | |
16 | + visible={statusData.visible} | |
17 | + maskClosable={false} | |
18 | + onCancel={handleCancel} | |
19 | + footer={[]} | |
20 | + className={styles.modal} | |
21 | + > | |
22 | + <List | |
23 | + size="large" | |
24 | + style={{height: '300px', overflow: 'scroll'}} | |
25 | + dataSource={statusData?.data || []} | |
26 | + renderItem={(item: any) => <List.Item style={{justifyContent: 'center'}}>{item.shopName}</List.Item>} | |
27 | + /> | |
28 | + <div | |
29 | + style={{ | |
30 | + textAlign: 'center', | |
31 | + marginTop: 12, | |
32 | + height: 32, | |
33 | + lineHeight: '32px', | |
34 | + }} | |
35 | + ><Button type="primary" onClick={handleCancel}>返回</Button> | |
36 | + </div> | |
37 | + </Modal> | |
38 | + ); | |
39 | +} | |
0 | 40 | \ No newline at end of file | ... | ... |
src/pages/order3/RetailTaskConfiguration/index.less
0 → 100644
1 | +.title { | |
2 | + font-size: 18px; | |
3 | + color: #333; | |
4 | + font-weight: 600; | |
5 | +} | |
6 | + | |
7 | +.warp { | |
8 | + display: flex; | |
9 | + direction: row; | |
10 | + align-items: flex-start; | |
11 | + justify-content: flex-start; | |
12 | +} | |
13 | + | |
14 | +.wrapItem { | |
15 | + display: inline-block; | |
16 | +} | |
17 | + | |
18 | +.text { | |
19 | + color: #333; | |
20 | + font-size: 14px; | |
21 | + font-weight: 600; | |
22 | +} | |
23 | + | |
24 | +.modal { | |
25 | + height: 400px; | |
26 | +} | |
27 | + | |
28 | +.line { | |
29 | + width: 4px; | |
30 | + height: 20px; | |
31 | + background-color: #4189FD; | |
32 | + border-radius: 2px; | |
33 | + display: inline-block; | |
34 | +} | |
35 | + | |
36 | +.lineText { | |
37 | + font-size: 18px; | |
38 | + color: #262626; | |
39 | + font-weight: bold; | |
40 | + display: inline-block; | |
41 | + margin-left: 8px; | |
42 | +} | |
43 | + | |
44 | +.lineWrap { | |
45 | + display: flex; | |
46 | + align-items: center; | |
47 | +} | |
48 | + | |
49 | +.lineSpace { | |
50 | + margin-bottom: 10px; | |
51 | +} | |
0 | 52 | \ No newline at end of file | ... | ... |
src/pages/order3/RetailTaskConfiguration/index.tsx
... | ... | @@ -4,6 +4,8 @@ import { PageHeaderWrapper } from '@ant-design/pro-layout'; |
4 | 4 | import { createStore } from '@/hooks/moz'; |
5 | 5 | import store from './store'; |
6 | 6 | import List from './components/List'; |
7 | +import ShopModal from './components/ShopModal'; | |
8 | +import EditModal from './components/EditMidal'; | |
7 | 9 | |
8 | 10 | export const { Provider, useStore } = createStore(store); |
9 | 11 | |
... | ... | @@ -12,10 +14,12 @@ function SubsidizedInterest() { |
12 | 14 | return ( |
13 | 15 | <PageHeaderWrapper title="线索到店零售占比配置"> |
14 | 16 | <Card> |
15 | - <Row justify="space-between" style={{ marginBottom: 20 }}> | |
17 | + <Row justify="end" style={{ marginBottom: 20 }}> | |
16 | 18 | <Button type="primary" onClick={() => setVisible(true)}>新增</Button> |
17 | 19 | </Row> |
18 | 20 | <List /> |
21 | + <ShopModal /> | |
22 | + <EditModal /> | |
19 | 23 | </Card> |
20 | 24 | </PageHeaderWrapper> |
21 | 25 | ); | ... | ... |
src/pages/order3/RetailTaskConfiguration/store.ts
1 | 1 | import React, { useState } from 'react'; |
2 | 2 | import usePagination from '@/hooks/usePagination'; |
3 | 3 | import useInitial from '@/hooks/useInitail'; |
4 | -import { getRetailListApi} from './api'; | |
5 | -import {getShopApi} from '@/common/api'; | |
4 | +import { getRetailListApi, List, ShopList} from './api'; | |
5 | + | |
6 | +interface StatusData { | |
7 | + visible: boolean | |
8 | + data: ShopList[] | |
9 | +} | |
6 | 10 | |
7 | 11 | export default function useStore() { |
8 | - const { list, setParams, setLoading, loading, paginationConfig, innerParams } = usePagination(getRetailListApi, {}); | |
12 | + const { data, setParams, setLoading, loading } = useInitial(getRetailListApi, [], null); | |
9 | 13 | const [visible, setVisible] = useState<boolean>(false); |
14 | + const [current, setCurrent] = useState<List>({}); | |
15 | + const [statusData, setStatusData] = useState<StatusData>({visible: false, data: []}); | |
10 | 16 | return { |
11 | - list, | |
17 | + data, | |
12 | 18 | setParams, |
13 | - innerParams, | |
14 | 19 | setLoading, |
15 | 20 | loading, |
16 | - paginationConfig, | |
17 | 21 | visible, |
18 | - setVisible | |
22 | + setVisible, | |
23 | + current, | |
24 | + setCurrent, | |
25 | + statusData, | |
26 | + setStatusData | |
19 | 27 | }; |
20 | 28 | } |
21 | 29 | \ No newline at end of file | ... | ... |