Selaa lähdekoodia

refactor: 目录结构调整

lanjianrong 5 vuotta sitten
vanhempi
commit
25eeecaeb0

+ 2 - 0
config/config.js

@@ -1,6 +1,7 @@
 // https://umijs.org/config/
 
 const CompressionWebpackPlugin = require('compression-webpack-plugin')
+const AntdDayjsWebpackPlugin = require('antd-dayjs-webpack-plugin')
 const prodGzipList = ['js', 'css']
 
 import { defineConfig } from 'umi'
@@ -69,6 +70,7 @@ export default defineConfig({
   ],
   chainWebpack(config) {
     config.plugin('windicss').use(windicss)
+    config.plugin('antd-dayjs-webpack-plugin').use(AntdDayjsWebpackPlugin)
 
     if (REACT_APP_ENV === 'prod') {
       config.merge({

+ 6 - 0
config/routes.js

@@ -73,6 +73,12 @@ export default [
                     name: 'business',
                     icon: 'icon-usd-circle',
                     component: './customer/Business'
+                  },
+                  {
+                    path: '/customer/test',
+                    name: 'test',
+                    icon: 'icon-usd-circle',
+                    component: './customer/Test'
                   }
                 ]
               },

+ 1 - 0
package.json

@@ -92,6 +92,7 @@
     "@umijs/preset-react": "1.8.12",
     "@umijs/yorkie": "^2.0.3",
     "agent-base": "^6.0.2",
+    "antd-dayjs-webpack-plugin": "^1.0.6",
     "babel-eslint": "^10.1.0",
     "babel-plugin-import": "^1.13.3",
     "carlo": "^0.9.46",

+ 24 - 0
src/components/EditableForm/src/components/ChangeDatePicker.jsx

@@ -0,0 +1,24 @@
+import React, { forwardRef, useState } from 'react'
+import { DatePicker } from 'antd'
+
+const ChangeDatePicker = props => {
+  const { defaultValue, refinstance, onSave } = props
+  const [val, setVal] = useState(defaultValue)
+  const handleOnChange = dayjs => {
+    const date = dayjs.format('YYYY-MM-DD HH:mm:ss')
+    setVal(date)
+  }
+  return (
+    <DatePicker
+      className="w-full"
+      placeholder="请选择日期"
+      ref={refinstance}
+      allowClear
+      defaultValue={defaultValue}
+      onBlur={() => onSave({ currentTarget: { value: val } })}
+      onChange={handleOnChange}
+    />
+  )
+}
+
+export default forwardRef((props, ref) => <ChangeDatePicker {...props} refinstance={ref} />)

+ 1 - 1
src/models/contact.js

@@ -6,7 +6,7 @@ import {
 import { queryTagList } from '@/services/customer'
 
 export default {
-  namespace: 'contact',
+  namespace: 'client',
   state: {
     personTags: [], // 个人标签
     personTagColorMap: {}, // 个人标签的颜色映射关系

+ 4 - 3
src/models/refresh.js

@@ -1,12 +1,13 @@
 export default {
   namespace: 'refresh',
   state: {
-    contactlist: false,
-    contact: false,
+    contactList: false,
+    client: false,
     company: false,
     longle: false,
     department: false,
-    stafflist: false
+    staffList: false,
+    business: false
   },
   // 用于处理异步操作和业务逻辑,由action触发,但不能修改state
   effects: {

+ 77 - 0
src/pages/Customer/Business/components/AddBusiness.jsx

@@ -0,0 +1,77 @@
+import {
+  ModalForm,
+  ProFormDatePicker,
+  ProFormDependency,
+  ProFormSelect,
+  ProFormText
+} from '@ant-design/pro-form'
+import ModalDrag from '@/components/DragmModal'
+import { Button, Row, Col, message } from 'antd'
+import { Plus } from '@icon-park/react'
+import React from 'react'
+import { addBusiness } from '@/services/customer'
+
+const AddBusiness = props => {
+  const { groupList = [] } = props
+  const layout = {
+    layout: 'horizontal',
+    labelCol: { flex: '100px' }
+  }
+  return (
+    <ModalForm
+      {...layout}
+      trigger={
+        <Button type="primary" className="flex items-center">
+          <Plus className="mr-1" />
+          商机
+        </Button>
+      }
+      title={<ModalDrag title={'添加商机'} />}
+      onFinish={async values => {
+        try {
+          await addBusiness(values)
+          return true
+        } catch (error) {
+          message.error('添加失败,请重试')
+          return false
+        }
+      }}>
+      <ProFormText name="name" label="商机名称" required />
+      <Row>
+        <Col span={12}>
+          <ProFormSelect
+            fieldProps={{ options: groupList }}
+            name="businessGroupId"
+            label="商机状态组"
+            required
+          />
+        </Col>
+        <Col span={12}>
+          <ProFormDependency name={['businessGroupId']}>
+            {({ businessGroupId }) => (
+              <ProFormSelect
+                name="businessStatusId"
+                label="商机状态"
+                options={groupList
+                  .find(item => item.value === businessGroupId)
+                  ?.items?.map(i => ({ label: i.name, value: i.id }))}
+                required
+              />
+            )}
+          </ProFormDependency>
+        </Col>
+      </Row>
+      <Row>
+        <Col span={12}>
+          <ProFormText name="price" label="商机金额(元)" required />
+        </Col>
+        <Col span={12}>
+          <ProFormDatePicker name="finalTime" label="预计成交" required />
+        </Col>
+      </Row>
+      <ProFormText name="remark" label="备注" />
+    </ModalForm>
+  )
+}
+
+export default AddBusiness

+ 141 - 0
src/pages/Customer/Business/components/BusinessDetail/Form.jsx

@@ -0,0 +1,141 @@
+import { Row, Col } from 'antd'
+import React from 'react'
+import EditableForm from '@/components/EditableForm'
+import ChangeCompanyInput from '@/pages/customer/Contact/components/ChangeCompany'
+import { ChangeCompMap } from '@/pages/Customer/Contact/components/ChangeCompany'
+
+const Form = props => {
+  const { business } = props
+
+  const changeCopInputData = {
+    customerName: business.companyName,
+    customerId: business.companyId,
+    targetName: business.name,
+    dataId: business.id,
+    actionPayload: ChangeCompMap.BUSINESS.key,
+    title: ChangeCompMap.BUSINESS.title
+  }
+
+  const columns = [
+    {
+      dataIndex: 'name',
+      label: '商机名称',
+      span: 24
+    },
+    {
+      dataIndex: 'companyName',
+      label: '客户名称',
+      editCellType: 'custom',
+      customCell: <ChangeCompanyInput dataSource={changeCopInputData} />,
+      span: 24
+    },
+    {
+      dataIndex: 'gender',
+      label: '性别',
+      editCellType: 'select',
+      span: 12
+    },
+    {
+      dataIndex: 'niceName',
+      label: '昵称',
+      span: 12
+    },
+    {
+      dataIndex: 'telephone',
+      label: '手机',
+      span: 12
+    },
+    {
+      dataIndex: 'qq',
+      label: 'QQ',
+      span: 12
+    },
+    {
+      dataIndex: 'phone',
+      label: '电话',
+      span: 12
+    },
+    {
+      dataIndex: 'email',
+      label: '邮箱',
+      span: 12
+    },
+    {
+      dataIndex: 'fax',
+      label: '传真',
+      span: 12
+    },
+    {
+      dataIndex: 'department',
+      label: '部门',
+      span: 12
+    },
+    {
+      dataIndex: 'position',
+      label: '职务',
+      span: 12
+    },
+    {
+      dataIndex: 'office',
+      label: '办公室',
+      span: 24,
+      editCellType: 'textarea'
+    },
+    {
+      dataIndex: 'address',
+      label: '联系人地址',
+      span: 24,
+      editCellType: 'textarea'
+    },
+    {
+      dataIndex: 'ride',
+      label: '联系人乘车',
+      span: 24,
+      editCellType: 'textarea'
+    },
+    {
+      dataIndex: 'landmarks',
+      label: '联系人地标',
+      span: 24,
+      editCellType: 'textarea'
+    },
+    {
+      dataIndex: 'price',
+      label: '商机金额',
+      span: 24,
+      editCellType: 'textarea'
+    },
+    {
+      dataIndex: 'remark',
+      label: '备注',
+      span: 24,
+      editCellType: 'textarea'
+    }
+  ]
+
+  return (
+    <div>
+      <Row>
+        <Col span={8}>
+          <h2 className="text-gray-500">联系人</h2>
+        </Col>
+        {/* className={[ 'text-right', styles.textMuted ]} */}
+        <Col span={16} className="zh-text-right zh-gray">
+          {business.staffName} 创建于{business.createTime}
+        </Col>
+        <Col span={22}>
+          <h2 className="text-2xl pb-4">{business.clientName}</h2>
+        </Col>
+        <Col span={2} />
+      </Row>
+      <EditableForm
+        dataSource={business}
+        columns={columns}
+        tartgetUrl="/client/update"
+        type="contact"
+      />
+    </div>
+  )
+}
+
+export default Form

+ 76 - 0
src/pages/Customer/Business/components/BusinessDetail/LowerList.jsx

@@ -0,0 +1,76 @@
+import { Tabs, List, Row, Col } from 'antd'
+import React from 'react'
+import { dayjsFormat } from '@/utils/utils'
+
+const ContanctLowerList = props => {
+  const {
+    log: { log: logs = [] },
+    servicelist
+  } = props
+  const servicelogEnum = {
+    1: '上门服务',
+    2: '电话拜访',
+    3: '其他事项',
+    4: '在线服务'
+  }
+  const { TabPane } = Tabs
+  return (
+    <div>
+      <div className="zh-pd-left-15">
+        <Tabs>
+          <TabPane tab="服务记录" key="服务记录">
+            <div className="sheet-panel-record">
+              <Row>
+                <Col span="8">类型/时间</Col>
+                <Col span="16">服务内容</Col>
+              </Row>
+              <List
+                dataSource={servicelist}
+                split={false}
+                renderItem={item => (
+                  <List.Item>
+                    <Row className="zh-width-100P">
+                      <Col span="8">
+                        <a className="zh-mg-right-3">@{item.staffName}</a>
+                        {servicelogEnum[item.status]}
+                        <List.Item.Meta description={dayjsFormat(item.date, 'MM-DD HH:mm:ss')} />
+                      </Col>
+                      <Col span="16" className="whitespace-pre-line">
+                        {item.mark}
+                      </Col>
+                    </Row>
+                  </List.Item>
+                )}
+              />
+            </div>
+          </TabPane>
+          <TabPane tab="日志" key="日志">
+            <div className="sheet-panel-record">
+              <List
+                dataSource={logs}
+                split={false}
+                renderItem={item => (
+                  <List.Item>
+                    <a>@{item.operatorName}</a> {item.content}
+                    <List.Item.Meta
+                      // avatar=
+                      // title=
+                      // description={ dayjsFormat(item.createTime, 'YYYY-MM-DD')}
+                      description={dayjsFormat(item.createTime)}
+                    />
+                  </List.Item>
+                )}
+              />
+            </div>
+          </TabPane>
+        </Tabs>
+      </div>
+    </div>
+  )
+}
+
+export default ContanctLowerList
+
+// export default connect(({ refresh }) => ({
+//   shouldUpdate: refresh.contactLog
+// }))(ContanctLowerList)

+ 68 - 0
src/pages/Customer/Business/components/BusinessDetail/Record.jsx

@@ -0,0 +1,68 @@
+import React, { useState } from 'react'
+import { Modal, Form, Input, Radio, DatePicker, Switch } from 'antd'
+import { useForm } from 'antd/lib/form/Form'
+import { dayjsFormat } from '@/utils/utils'
+
+const AddRecord = props => {
+  const [form] = useForm()
+  const { TextArea } = Input
+  const { visible, onCancel, onConfirm, loading } = props
+  const [showExtraItem, setShowExtraItem] = useState(false)
+  const onChange = value => {
+    // const { value = '' } = e.currentTarget || e.target
+    setShowExtraItem(value)
+  }
+  return (
+    <div>
+      <Modal
+        title="添加服务记录"
+        visible={visible}
+        confirmLoading={loading}
+        onOk={() => {
+          form.validateFields().then(values => {
+            const newVals = { ...values }
+            form.resetFields()
+            if (values.date) {
+              newVals.date = dayjsFormat(values.date, 'YYYY-MM-DD HH:mm:ss')
+            }
+            onConfirm(newVals)
+          })
+        }}
+        onCancel={onCancel}
+        width={500}>
+        <Form form={form} layout="vertical">
+          <Form.Item name="status" rules={[{ required: true, message: '请选择类型' }]}>
+            <Radio.Group>
+              <Radio value={1}>上门服务</Radio>
+              <Radio value={2}>电话拜访</Radio>
+              <Radio value={3}>其他事项</Radio>
+              <Radio value={4}>在线服务</Radio>
+            </Radio.Group>
+          </Form.Item>
+          <Form.Item name="date" label="时间">
+            <DatePicker style={{ width: '100%' }} />
+          </Form.Item>
+          <Form.Item name="mark" label="内容">
+            <TextArea rows={3} />
+          </Form.Item>
+          <div className="zh-pd-bottom-20">
+            <Switch onChange={onChange} />
+            <span className="zh-mg-left-3">添加提醒</span>
+          </div>
+          {showExtraItem ? (
+            <>
+              <Form.Item name="deadline" label="提醒限期">
+                <DatePicker style={{ width: '100%' }} />
+              </Form.Item>
+              <Form.Item name="remark" label="备注">
+                <TextArea rows={2} />
+              </Form.Item>
+            </>
+          ) : null}
+        </Form>
+      </Modal>
+    </div>
+  )
+}
+
+export default AddRecord

+ 120 - 0
src/pages/Customer/Business/components/BusinessDetail/TabList.jsx

@@ -0,0 +1,120 @@
+import { message, Tabs, Button } from 'antd'
+import React, { useEffect, useState, useRef } from 'react'
+import consts from '@/consts'
+import SvgIcon from '@/components/SvgIcon'
+import { apiAddService } from '@/services/customer'
+import AddRecord from './Record'
+import { useDispatch } from 'dva'
+import ProTable from '@ant-design/pro-table'
+
+const ContanctTabList = ({ clientId, software }) => {
+  const dispatch = useDispatch()
+  const [state, setState] = useState({
+    params: {},
+    visible: false
+  })
+  const tRef = useRef()
+  const columns = [
+    {
+      title: '锁号',
+      dataIndex: 'keyNum'
+    },
+    {
+      title: '产品',
+      dataIndex: 'product'
+    },
+    {
+      title: '状态',
+      dataIndex: 'statusT'
+    },
+    {
+      title: '责任人',
+      dataIndex: 'client'
+    }
+  ]
+  const [addService, setAddService] = useState({
+    visible: false,
+    loading: false
+  })
+
+  const handleAddService = async values => {
+    setAddService({ ...addService, loading: true })
+    const { code = -1 } = await apiAddService({ ...values, clientId })
+    if (code === consts.RET_CODE.SUCCESS) {
+      message.success('新增成功')
+      setState({ ...state, data: values })
+    }
+    // 请求完了
+    setAddService({ ...addService, loading: false, visible: false })
+    // 刷新数据
+    dispatch({
+      type: 'refresh/commitAction',
+      payload: 'client'
+    })
+    // await initData()
+  }
+  useEffect(() => {
+    setState({ ...state, params: { ...state.params, clientId } })
+  }, [clientId])
+  const { TabPane } = Tabs
+  return (
+    <div>
+      <div className="zh-pd-left-15">
+        {/* <Tabs onChange={callback} type="card"> */}
+        <Tabs>
+          {/* <TabPane tab="养护云造价" key="养护云造价">
+            <div className="sheet-right-panel">
+              养护云造价
+              <Table columns={curingcolumns} dataSource={curinglist} pagination={false} />
+            </div>
+          </TabPane>
+          <TabPane tab="大司空云计价" key="大司空云计价">
+            <div className="sheet-right-panel">大司空云计价</div>
+          </TabPane> */}
+          <TabPane tab="软件锁" key="软件锁">
+            <div className="sheet-right-panel">
+              <ProTable
+                columns={columns}
+                search={false}
+                toolBarRender={false}
+                params={state.params}
+                actionRef={tRef}
+                rowKey={record => record.id}
+                dataSource={software.longle}
+                pagination={false}
+              />
+            </div>
+          </TabPane>
+          {/* <TabPane tab="通行账号" key="通行账号">
+            <div className="sheet-right-panel">通行账号</div>
+          </TabPane> */}
+        </Tabs>
+      </div>
+      <div className="sheet-btns zh-pd-left-15 zh-pd-top-15">
+        {/* <Button type="primary" ghost>
+          <SvgIcon type="icon-link" /> 绑定加密锁
+        </Button> */}
+        <Button
+          type="primary"
+          ghost
+          size="small"
+          onClick={() => setAddService({ ...setAddService, visible: true })}>
+          <SvgIcon type="icon-plus" /> 添加服务记录
+        </Button>
+      </div>
+      <AddRecord
+        onConfirm={handleAddService}
+        loading={addService.loading}
+        visible={addService.visible}
+        onCancel={() => setAddService({ ...addService, visible: false })}
+      />
+    </div>
+  )
+}
+
+export default ContanctTabList
+
+// export default connect(({ curinglist, loading }) => ({
+//   curinglistName: curinglist,
+//   getlist: loading.effects['curinglist/fetch']
+// }))(ContanctTabList)

+ 97 - 0
src/pages/Customer/Business/components/BusinessDetail/index.jsx

@@ -0,0 +1,97 @@
+import { Row, Col, Spin, Tooltip } from 'antd'
+import React, { useState, useEffect } from 'react'
+// import Form from './Form.jsx'
+// import TabList from './TabList.jsx'
+// import LowerList from './LowerList.jsx'
+import { getBusinessDetailById } from '@/services/customer'
+import consts from '@/consts'
+import { connect } from 'dva'
+import { Up, Down } from '@icon-park/react'
+import { FlipOverEnum, useFlipOver } from '@/hooks/web/useFlipOver'
+
+const Detail = props => {
+  const { dispatch, shouldUpdate, visible, dataId = '', orderIds = [] } = props
+
+  const [state, setState] = useState({
+    loading: false,
+    business: {},
+    log: [],
+    serviceLog: [],
+    software: {}
+  })
+
+  const initData = async id => {
+    setState({ ...state, loading: true })
+    const {
+      data: { business = {}, log = [] },
+      code = -1
+    } = await getBusinessDetailById({ id })
+    if (code === consts.RET_CODE.SUCCESS) {
+      if (shouldUpdate) {
+        dispatch({
+          type: 'refresh/commitAction',
+          payload: 'business'
+        })
+      }
+      setState({ ...state, business, log, loading: false })
+    }
+  }
+
+  const { flipFn, flipConsts } = useFlipOver(initData, dataId, orderIds)
+
+  useEffect(() => {
+    shouldUpdate && initData(dataId)
+  }, [shouldUpdate])
+
+  useEffect(() => {
+    visible && initData(dataId)
+  }, [visible])
+
+  return (
+    <Spin spinning={state.loading}>
+      <div className="sheet-box">
+        <Row>
+          <Col span={12} className="sheet-box-left">
+            {orderIds.length ? (
+              <div className="mb-3">
+                <Tooltip title="上一条记录">
+                  <Up
+                    fill={flipConsts.disableUpBtn ? '#BDBDBE' : '#886ab5'}
+                    size="20"
+                    onClick={() => flipFn(FlipOverEnum.UP)}
+                    className={[
+                      flipConsts.disableUpBtn ? 'cursor-not-allowed' : 'cursor-pointer'
+                    ].join(' ')}
+                  />
+                </Tooltip>
+                <Tooltip title="下一条记录">
+                  <Down
+                    fill={flipConsts.disableDownBtn ? '#BDBDBE' : '#886ab5'}
+                    size="20"
+                    onClick={() => flipFn(FlipOverEnum.DOWN)}
+                    className={[
+                      flipConsts.disableDownBtn ? 'cursor-not-allowed' : 'cursor-pointer',
+                      'ml-1'
+                    ].join(' ')}
+                  />
+                </Tooltip>
+              </div>
+            ) : null}
+            <div className="sheet-left-panel zh-pd-right-15">
+              {/* <Form business={state.business} /> */}
+            </div>
+          </Col>
+          <Col span={12} className="sheet-box-right">
+            {/* <TabList clientId={state.client.id} software={state.software} /> */}
+            {/* <LowerList log={state.log} servicelist={state.serviceLog} /> */}
+          </Col>
+        </Row>
+      </div>
+    </Spin>
+  )
+}
+
+export default connect(({ refresh, single }) => ({
+  visible: single.drawer.visible,
+  shouldUpdate: refresh.business
+}))(Detail)

+ 128 - 6
src/pages/Customer/Business/index.jsx

@@ -1,13 +1,135 @@
-import React from 'react'
+import React, { useState } from 'react'
 import { Select } from 'antd'
+import { useRequest } from 'umi'
+import { getBusinessGroupList, getBusinessList } from '@/services/customer'
+import ProTable from '@ant-design/pro-table'
+import consts from '@/consts'
+import AddBusiness from './components/AddBusiness'
+import Detail from './components/BusinessDetail'
+import { useModal } from '@/components/Modal'
 
 const Business = () => {
+  const { toggleDrawer, setDrawerProps } = useModal()
+  const [state, setState] = useState({
+    groupList: [],
+    activeItem: null
+  })
+  useRequest(getBusinessGroupList, {
+    onSuccess: result => {
+      setState({
+        ...state,
+        groupList: result.map(item => ({
+          label: item.name,
+          value: item.id,
+          items: item.status
+        })),
+        activeItem: result[0]?.id
+      })
+    }
+  })
+
+  // 展示drawer
+  const showDrawer = id => {
+    setDrawerProps({
+      closable: true,
+      onClose: () => toggleDrawer(),
+      bodyStyle: {
+        height: '100vh',
+        overflowY: 'hidden'
+      },
+      children: <Detail dataId={id} orderIds={state.orderIds} />
+    })
+    toggleDrawer()
+  }
+
+  const columns = [
+    {
+      dataIndex: 'name',
+      title: '商机名称',
+      render: (text, record) => (
+        <span
+          onClick={() => showDrawer(record.id)}
+          className="text-primary cursor-pointer hover:text-[#967bbd]">
+          {text}
+        </span>
+      )
+    },
+    {
+      dataIndex: 'customerName',
+      title: '客户名称'
+    },
+    {
+      dataIndex: 'businessGroupName',
+      title: '装机状态组'
+    },
+    {
+      dataIndex: 'name',
+      title: '商机阶段'
+    },
+    {
+      dataIndex: 'responsible',
+      title: '负责人'
+    },
+    {
+      dataIndex: 'remark',
+      title: '备注'
+    },
+    {
+      dataIndex: 'updateTime',
+      title: '最后跟进时间'
+    },
+    {
+      dataIndex: 'createTime ',
+      title: '创建时间'
+    },
+    {
+      dataIndex: 'staffName',
+      title: '创建人'
+    }
+  ]
   return (
-    <div className="h-full w-full flex flex-row">
-      <div className="h-full w-max-234px rounded-4px shadow-card">
-        <div className="p-4 border-b-1 border-solid border-black border-opacity-10 bg-[#f7f9fa] justify-around text-left">
-          <span>商机漏斗</span>
-          <Select />
+    <div className="h-full w-full flex flex-row justify-between">
+      <div className="h-full w-3/20 rounded-4px shadow-card">
+        <div className="p-4 border-b-1 border-solid border-black border-opacity-10 bg-[#f7f9fa] flex justify-around items-center">
+          <div className="w-1/2 text-md">商机漏斗</div>
+          <div className="w-1/2">
+            {state.activeItem && (
+              <Select
+                size="small"
+                className="w-full"
+                bordered={false}
+                options={state.groupList}
+                defaultValue={state.activeItem}
+              />
+            )}
+          </div>
+        </div>
+        <div className="p-4 bg-white " style={{ height: 'calc(100% - 1rem*2 - 20px)' }}>
+          11
+        </div>
+      </div>
+      <div className="w-17/20">
+        <div className="ml-8 bg-white p-4 shadow-card">
+          <ProTable
+            bordered
+            rowKey={record => record.id}
+            columns={columns}
+            request={async (params, sort, filter) => {
+              const {
+                code = -1,
+                data: { business = [], total = 0 }
+              } = await getBusinessList({ ...params, ...sort, ...filter })
+              return {
+                data: business,
+                success: code === consts.RET_CODE.SUCCESS,
+                total
+              }
+            }}
+            search={false}
+            toolbar={{
+              actions: [<AddBusiness key="add" groupList={state.groupList} />]
+            }}
+          />
         </div>
       </div>
     </div>

+ 1 - 1
src/pages/Customer/Company/components/PersonLabel/const.js

@@ -4,7 +4,7 @@ export const DropdownTypeEnum = {
 }
 // 数据类型
 export const TagDataTypeEnum = {
-  CONTACT: 1, // 联系人
+  CLIENT: 1, // 联系人
   COMPANY: 2 // 客户
 }
 // 标签类型

+ 10 - 10
src/pages/Customer/Company/components/PersonLabel/index.jsx

@@ -22,18 +22,18 @@ const PersonLabel = props => {
     modeType = LabelModeType.column,
     checkCallBack,
     dispatch,
-    loadingContact,
+    loadingClient,
     loadingCompany
   } = props
   const [dropdownType, setDropdownType] = useState(DropdownTypeEnum.check)
 
   const tags = useMemo(() => {
-    if (tagColumn === TagDataTypeEnum.CONTACT) {
+    if (tagColumn === TagDataTypeEnum.CLIENT) {
       // 联系人
       if (tagType === TagTypeEnum.PERSONTAG) {
-        return props.contactPersonTags
+        return props.clientPersonTags
       }
-      return props.contactTeamTags
+      return props.clientTeamTags
     }
     if (tagColumn === TagDataTypeEnum.COMPANY) {
       // 客户
@@ -43,7 +43,7 @@ const PersonLabel = props => {
       return props.companyTeamTags
     }
     return []
-  }, [tagType, tagColumn, tagColumn === TagDataTypeEnum.CONTACT ? loadingContact : loadingCompany])
+  }, [tagType, tagColumn, tagColumn === TagDataTypeEnum.CLIENT ? loadingClient : loadingCompany])
 
   const [checkedIds, setCheckedIds] = useState([])
   useEffect(() => {
@@ -55,7 +55,7 @@ const PersonLabel = props => {
   const [showOkBtn, setShowOkBtn] = useState(false)
   const initData = () => {
     dispatch({
-      type: `${tagColumn === TagDataTypeEnum.COMPANY ? 'company' : 'contact'}/fetchTags`,
+      type: `${tagColumn === TagDataTypeEnum.COMPANY ? 'company' : 'client'}/fetchTags`,
       payload: { tagType, tagColumn }
     })
   }
@@ -259,11 +259,11 @@ const DropDownMenu = props => {
   )
 }
 
-export default connect(({ contact, company, loading }) => ({
-  contactPersonTags: contact.personTags,
-  contactTeamTags: contact.teamTags,
+export default connect(({ client, company, loading }) => ({
+  clientPersonTags: client.personTags,
+  clientTeamTags: client.teamTags,
   companyPersonTags: company.personTags,
   companyTeamTags: company.teamTags,
-  loadingContact: loading.models.contact,
+  loadingClient: loading.models.client,
   loadingCompany: loading.models.company
 }))(PersonLabel)

+ 13 - 5
src/pages/Customer/Contact/components/AddCustomerModal/index.jsx

@@ -5,8 +5,9 @@ import LazyCascader from '@/components/LazyCascader'
 import { ModalForm, ProFormText, ProFormCheckbox } from '@ant-design/pro-form'
 import { PlusOutlined } from '@ant-design/icons'
 import { connect } from 'dva'
+import { ChangeCompMap } from '../ChangeCompany'
 
-const AddCustomerModal = ({ clientId, onConfirm, dispatch, natures }) => {
+const AddCustomerModal = ({ dataId, dataType, onConfirm, dispatch, natures }) => {
   const formRef = useRef()
   const [modalVisible, setModalVisible] = useState(false)
   const layout = {
@@ -21,7 +22,7 @@ const AddCustomerModal = ({ clientId, onConfirm, dispatch, natures }) => {
     }
     !natures.length && getNatures()
   }, [])
-  const [showContactItem, setShowContactItem] = useState(false)
+  const [showDataItem, setShowDataItem] = useState(false)
   return (
     <ModalForm
       formRef={formRef}
@@ -36,8 +37,15 @@ const AddCustomerModal = ({ clientId, onConfirm, dispatch, natures }) => {
             type="primary"
             key="2"
             onClick={() => {
-              setShowContactItem(true)
-              formRef.current && formRef.current.setFieldsValue({ clientId })
+              setShowDataItem(true)
+              const values = {}
+              if (dataType === ChangeCompMap.CLIENT.key) {
+                values.clientId = dataId
+              }
+              if (dataType === ChangeCompMap.BUSINESS.key) {
+                values.businessId = dataId
+              }
+              formRef.current && formRef.current.setFieldsValue(values)
               submit()
             }}>
             添加并关联
@@ -59,7 +67,7 @@ const AddCustomerModal = ({ clientId, onConfirm, dispatch, natures }) => {
         placeholder="请输入客户全称"
         rules={[{ required: true, message: '请输入客户全称' }]}
       />
-      {showContactItem ? <ProFormText name="clientId" hidden /> : null}
+      {showDataItem ? <ProFormText name={`${dataType}Id`} hidden /> : null}
       <Form.Item
         name="district"
         label="客户地区"

+ 25 - 5
src/pages/Customer/Contact/components/ChangeCompany/index.jsx

@@ -9,22 +9,41 @@ import { useDispatch } from 'dva'
 import consts from '@/consts'
 import CompanyDetail from '@/pages/customer/Company/components/CompanyDetail'
 
+export const ChangeCompMap = {
+  CLIENT: {
+    key: 'client',
+    title: '联系人'
+  },
+  BUSINESS: {
+    key: 'business',
+    title: '商机组'
+  }
+}
 const ChangeCompanyInput = props => {
   const dispatch = useDispatch()
   const { toggleModal, setModalProps, toggleDrawer, setDrawerProps } = useModal()
   const {
-    dataSource: { customerId, clientId, customerName, clientName }
+    dataSource: {
+      customerId,
+      dataId,
+      customerName,
+      targetName,
+      actionPayload = ChangeCompMap.CLIENT.key,
+      title = '联系人'
+    }
   } = props
   const refreshClient = () => {
     dispatch({
       type: 'refresh/commitAction',
-      payload: 'contact'
+      payload: actionPayload
     })
   }
   const handleConnectCustomer = () => {
     setModalProps({
       width: '60vw',
-      modalRender: node => <SearchModal onSelect={refreshClient} node={node} clientId={clientId} />,
+      modalRender: node => (
+        <SearchModal onSelect={refreshClient} node={node} dataId={dataId} preUrl={actionPayload} />
+      ),
       onCancel: () => toggleModal()
     })
     toggleModal()
@@ -42,12 +61,13 @@ const ChangeCompanyInput = props => {
       title: '确认移除',
       children: (
         <p>
-          为联系人<span className="mr-3px ml-3px font-600">{clientName}</span>移除
+          为{title}
+          <span className="mr-3px ml-3px font-600">{targetName}</span>移除
           <span className="mr-3px ml-3px font-600">{customerName}</span>
         </p>
       ),
       onOk: async () => {
-        await apiDelCustomer({ id: clientId, customerId })
+        await apiDelCustomer(actionPayload, { id: dataId, customerId })
         refreshClient()
         toggleModal()
       },

+ 8 - 8
src/pages/Customer/Contact/components/ContactDetail/ContanctForm.jsx

@@ -16,14 +16,14 @@ const ContanctForm = props => {
   const changeCopInputData = {
     customerName: client.companyName,
     customerId: client.companyId,
-    clientName: client.clientName,
-    clientId: client.id
+    targetName: client.clientName,
+    dataId: client.id
   }
 
   const refreshClient = () => {
     dispatch({
       type: 'refresh/commitAction',
-      payload: 'contact'
+      payload: 'client'
     })
   }
 
@@ -209,9 +209,9 @@ const ContanctForm = props => {
   )
 }
 
-export default connect(({ contact }) => ({
-  personTagColorMap: contact.personTagColorMap,
-  personTags: contact.personTags,
-  teamTagColorMap: contact.teamTagColorMap,
-  teamTags: contact.teamTags
+export default connect(({ client }) => ({
+  personTagColorMap: client.personTagColorMap,
+  personTags: client.personTags,
+  teamTagColorMap: client.teamTagColorMap,
+  teamTags: client.teamTags
 }))(ContanctForm)

+ 2 - 2
src/pages/Customer/Contact/components/ContactDetail/ContanctTabList.jsx

@@ -50,7 +50,7 @@ const ContanctTabList = ({ clientId, software }) => {
     // 刷新数据
     dispatch({
       type: 'refresh/commitAction',
-      payload: 'contact'
+      payload: 'client'
     })
     // await initData()
   }
@@ -60,7 +60,7 @@ const ContanctTabList = ({ clientId, software }) => {
   const { TabPane } = Tabs
   return (
     <div>
-      <div className="zh-pd-left-15">
+      <div className="pl-15px">
         {/* <Tabs onChange={callback} type="card"> */}
         <Tabs>
           {/* <TabPane tab="养护云造价" key="养护云造价">

+ 2 - 2
src/pages/Customer/Contact/components/ContactDetail/index.jsx

@@ -30,7 +30,7 @@ const ContactDetail = props => {
       if (shouldUpdate) {
         dispatch({
           type: 'refresh/commitAction',
-          payload: 'contact'
+          payload: 'client'
         })
       }
       setState({ ...state, client, log, serviceLog, software, loading: false })
@@ -93,5 +93,5 @@ const ContactDetail = props => {
 
 export default connect(({ refresh, single }) => ({
   visible: single.drawer.visible,
-  shouldUpdate: refresh.contact
+  shouldUpdate: refresh.client
 }))(ContactDetail)

+ 4 - 4
src/pages/Customer/Contact/components/CustomerModal/index.jsx

@@ -10,7 +10,7 @@ import consts from '@/basic/consts'
 import { formatValues } from '@/components/LazyCascader'
 import { apiChangeCustomer } from '@/services/customer'
 
-const SearchModal = ({ clientId, onSelect }) => {
+const SearchModal = ({ dataId, onSelect, preUrl }) => {
   const dispatch = useDispatch()
   const scrollRef = useRef()
   const [state, setState] = useState({
@@ -72,9 +72,9 @@ const SearchModal = ({ clientId, onSelect }) => {
     }
   }
 
-  // 联系人关联客户
+  // 联系人/商机关联客户
   const connectCustomer = async customerId => {
-    const { code = -1 } = await apiChangeCustomer({ id: clientId, customerId })
+    const { code = -1 } = await apiChangeCustomer(preUrl, { id: dataId, customerId })
     if (code === consts.RET_CODE.SUCCESS) {
       onSelect()
       closeModal()
@@ -124,7 +124,7 @@ const SearchModal = ({ clientId, onSelect }) => {
         </Spin>
       </div>
       <div className={styles.modalFooter}>
-        <AddCustomerModal onConfirm={addConfirm} clientId={clientId} />
+        <AddCustomerModal onConfirm={addConfirm} dataId={dataId} dataType={preUrl} />
       </div>
     </div>
   )

+ 19 - 28
src/pages/Customer/Contact/index.jsx

@@ -261,7 +261,7 @@ const Contact = props => {
             dataId={record.id}
             tagIds={record.tagIds}
             tagType={TagTypeEnum.PERSONTAG}
-            tagColumn={TagDataTypeEnum.CONTACT}
+            tagColumn={TagDataTypeEnum.CLIENT}
             checkCallBack={refreshData}
             modeType={LabelModeType.column}>
             <Add theme="filled" size="20" fill="#868e96" className="cursor-pointer" />
@@ -297,7 +297,7 @@ const Contact = props => {
             dataId={record.id}
             tagIds={record.tagCooperationIds}
             tagType={TagTypeEnum.TEAMTAG}
-            tagColumn={TagDataTypeEnum.CONTACT}
+            tagColumn={TagDataTypeEnum.CLIENT}
             checkCallBack={refreshData}
             modeType={LabelModeType.column}>
             <Add theme="filled" size="20" fill="#868e96" className="cursor-pointer" />
@@ -428,21 +428,21 @@ const Contact = props => {
     // initData()
     if (!personTags.length) {
       dispatch({
-        type: 'contact/fetchTags',
-        payload: { tagType: TagTypeEnum.PERSONTAG, tagColumn: TagDataTypeEnum.CONTACT }
+        type: 'client/fetchTags',
+        payload: { tagType: TagTypeEnum.PERSONTAG, tagColumn: TagDataTypeEnum.CLIENT }
       })
     }
     if (!teamTags.length) {
       dispatch({
-        type: 'contact/fetchTags',
-        payload: { tagType: TagTypeEnum.TEAMTAG, tagColumn: TagDataTypeEnum.CONTACT }
+        type: 'client/fetchTags',
+        payload: { tagType: TagTypeEnum.TEAMTAG, tagColumn: TagDataTypeEnum.CLIENT }
       })
     }
     if (shouldUpdate) {
       tRef.current.reload()
       dispatch({
-        type: 'refresh/contact',
-        payload: 'contact'
+        type: 'refresh/commitAction',
+        payload: 'client '
       })
     }
   }, [shouldUpdate])
@@ -452,19 +452,10 @@ const Contact = props => {
     setState({ ...state, params: { ...state.params, search: value } })
   }
 
-  // const searchOnchange = e => {
-  //   const { value = '' } = e.target
-  //   tRef.current?.setFieldsValue({ search: value })
-  //   tRef.current?.submit()
-  //   // initData({ search: value })
-  // }
-  // const { run: handleSearchInputChange } = useDebounceFn(searchOnchange)
-
   const onDistrictChange = value => {
     tRef.current.reset()
     const [province = '', city = '', area = ''] = value
     setState({ ...state, params: { ...state.params, province, city, area } })
-    // initData({ province, city, area })
   }
 
   // 批量设置-确认触发
@@ -473,7 +464,7 @@ const Contact = props => {
       await apiBatchLink({
         tagIds: tagState.tagIds,
         dataIds: clientIds,
-        dataType: TagDataTypeEnum.CONTACT,
+        dataType: TagDataTypeEnum.CLIENT,
         tagType: TagTypeEnum.PERSONTAG
       })
     }
@@ -481,7 +472,7 @@ const Contact = props => {
       await apiBatchLink({
         tagIds: tagState.tagCooperationIds,
         dataIds: clientIds,
-        dataType: TagDataTypeEnum.CONTACT,
+        dataType: TagDataTypeEnum.CLIENT,
         tagType: TagTypeEnum.TEAMTAG
       })
     }
@@ -494,7 +485,7 @@ const Contact = props => {
         <div className="mr-5px">
           <PersonLabel
             tagType={TagTypeEnum.PERSONTAG}
-            tagColumn={TagDataTypeEnum.CONTACT}
+            tagColumn={TagDataTypeEnum.CLIENT}
             checkCallBack={toolbarOptionsChange}
             modeType={LabelModeType.toolbar}>
             {tagState.tagIds.length ? (
@@ -519,7 +510,7 @@ const Contact = props => {
         <div className="mr-5">
           <PersonLabel
             tagType={TagTypeEnum.TEAMTAG}
-            tagColumn={TagDataTypeEnum.CONTACT}
+            tagColumn={TagDataTypeEnum.CLIENT}
             checkCallBack={toolbarOptionsChange}
             modeType={LabelModeType.toolbar}>
             {tagState.tagCooperationIds.length ? (
@@ -605,11 +596,11 @@ const Contact = props => {
   )
 }
 
-export default connect(({ contact, refresh, loading }) => ({
-  personTagColorMap: contact.personTagColorMap,
-  personTags: contact.personTags,
-  teamTagColorMap: contact.teamTagColorMap,
-  teamTags: contact.teamTags,
-  loading: loading.models.contact,
-  shouldUpdate: refresh.contact
+export default connect(({ client, refresh, loading }) => ({
+  personTagColorMap: client.personTagColorMap,
+  personTags: client.personTags,
+  teamTagColorMap: client.teamTagColorMap,
+  teamTags: client.teamTags,
+  loading: loading.models.client,
+  shouldUpdate: refresh.client
 }))(Contact)

+ 5 - 0
src/pages/Customer/Test/index.jsx

@@ -0,0 +1,5 @@
+import React from 'react'
+
+export default function Index() {
+  return <div />
+}

+ 22 - 8
src/services/customer.js

@@ -112,8 +112,8 @@ export async function apiAddService(payload) {
  * @param {*} payload
  * @returns
  */
-export async function apiChangeCustomer(payload) {
-  const data = await request.post('/client/change/customer', { data: { ...payload } })
+export async function apiChangeCustomer(preUrl, payload) {
+  const data = await request.post(`/${preUrl}/change/customer`, { data: { ...payload } })
   return data
 }
 
@@ -122,8 +122,8 @@ export async function apiChangeCustomer(payload) {
  * @param {*} payload
  * @returns
  */
-export async function apiDelCustomer(payload) {
-  const data = await request.post('/client/customer/delete', { data: { ...payload } })
+export async function apiDelCustomer(preUrl, payload) {
+  const data = await request.post(`/${preUrl}/customer/delete`, { data: { ...payload } })
   return data
 }
 
@@ -168,10 +168,24 @@ export async function apiAddCompany(payload) {
 
 /** 获取商机组列表 */
 export async function getBusinessGroupList() {
-  return request.get('/BusinessGroup/list')
+  return request.get('/business/group/list')
 }
 
-/** 获取商机组状态列表 */
-export async function getBusinessGroupStatusList() {
-  return request.get('/BusinessGroup/liststatus')
+/** 获取商机列表 */
+export async function getBusinessList() {
+  return request.get('/business/list')
+}
+
+/** 新增商机 */
+export async function addBusiness(params) {
+  return request.post('/business/add', {
+    data: params
+  })
+}
+
+/** 获取商机详情 */
+export async function getBusinessDetailById(params) {
+  return request.get('/business/detail', {
+    params
+  })
 }