Commit d03a9f2ff367dba0d99f5f11680fdf8dc05a542a

Authored by 舒述军
1 parent 0a763d83

线索目标接通配置

config/routers/crm_new.ts
... ... @@ -77,8 +77,12 @@ export default [
77 77 path: '/crm/threePartyPlatformClue', // 三方平台线索配置
78 78 component: './crm_new/ThreePartyPlatformClue',
79 79 },
80   - {
  80 + {
81 81 path: '/crm/closeClue', // 暂停区域线索站岗分配
82 82 component: './crm_new/Settings/subpages/CloseClue',
83 83 },
  84 + {
  85 + path: '/crm/clueConnect', // 线索有效接通配置
  86 + component: './crm_new/CluesConnectTargetEffectively',
  87 + },
84 88 ];
85 89 \ No newline at end of file
... ...
src/pages/crm_new/CluesConnectTargetEffectively/api.ts 0 → 100644
  1 +import request from '@/utils/request';
  2 +import { CRM_HOST } from '@/utils/host';
  3 +
  4 +export interface Result {
  5 + id?: number // 配置id
  6 + dialAims?: number // 线索接通目标
  7 + displayName?: string // 显示名称
  8 + shopList?: ShopList[] // 门店列表
  9 +}
  10 +
  11 +export interface ShopList {
  12 + shopId?: number // 门店id
  13 + shopName?: string // 门店名称
  14 +}
  15 +
  16 +/** 查询线索拨通目标配置列表 */
  17 +export function getConfigApi() {
  18 + return request.get<Result[]>(`${CRM_HOST}/erp/clue/dial/aims/config/list`);
  19 +}
  20 +
  21 +/** 查询线索拨通目标已配置的门店 */
  22 +export function getHaveShopListApi() {
  23 + return request.get<number[]>(`${CRM_HOST}/erp/clue/dial/aims/config/already/exists/shopIds`);
  24 +}
  25 +
  26 +/** 保存线索拨通目标配置 */
  27 +export function saveConfigApi(params: Result) {
  28 + return request.post<Result>(`${CRM_HOST}/erp/clue/dial/aims/config/save`, params);
  29 +}
  30 +
  31 +/** 删除线索拨通目标配置 */
  32 +export function deleteConfigApi(id?: number) {
  33 + return request.post<Result>(`${CRM_HOST}/erp/clue/dial/aims/config/delete`, {id}, {contentType: 'form-urlencoded'});
  34 +}
0 35 \ 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({shopId: value[0]?.value || 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="end" 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
... ...