Преглед изворни кода

feat: 客户接入标签功能

lanjianrong пре 5 година
родитељ
комит
e672682ed6

+ 6 - 0
src/basic/css/common.scss

@@ -464,3 +464,9 @@
   width: 12px;
   height: 12px;
 }
+
+.zh-container {
+  height: calc(100vh - 48px - 48px);
+  overflow: hidden;
+  background-color: #ffffff;
+}

+ 3 - 3
src/hooks/useAutoTable.js

@@ -46,7 +46,7 @@ export function useTableScroll(columnsRef, selectKeysRef) {
 
   // const toolbarEl = null
   async function calcTableHeight() {
-    const tableEl = document.getElementsByClassName('ant-pro-table')[0]
+    const tableEl = document.getElementsByClassName('ant-table-wrapper')[0]
     if (!tableEl) return
 
     if (!bodyEl) {
@@ -106,13 +106,13 @@ export function useTableScroll(columnsRef, selectKeysRef) {
     // console.log('footerHeight', footerHeight)
     // console.log('headerHeight', headerHeight)
     setTableHeight(height)
-    bodyEl && (bodyEl.style.height = `${height}px`)
+    bodyEl && (bodyEl.style.minHeight = `${height}px`)
   }
 
   function redoHeight() {
     calcTableHeight()
   }
-  const { run: debounceRedoHeight } = useDebounceFn(redoHeight, { wait: 100 })
+  const { run: debounceRedoHeight } = useDebounceFn(redoHeight, { wait: 280 })
 
   // Greater than animation time 280
   useWindowSizeFn(calcTableHeight, 280)

+ 29 - 15
src/models/company.js

@@ -1,34 +1,48 @@
 import consts from '@/consts'
-import { queryCompany } from '@/services/company'
+import { TagTypeEnum } from '@/pages/Customer/Company/components/PersonLabel'
+import { LabelStatusColorMap } from '@/pages/Customer/Company/components/PersonLabel'
+import { queryTagList } from '@/services/customer'
 
 export default {
   namespace: 'company',
   state: {
-    data: [],
-    total: 0
+    personTags: [], // 个人标签
+    personTagColorMap: {}, // 个人标签的颜色映射关系
+    teamTags: [], // 协作标签
+    teamTagColorMap: {} // 协作标签的颜色映射关系
   },
   // 用于处理异步操作和业务逻辑,由action触发,但不能修改state
   effects: {
-    *fetch({ payload }, { call, put }) {
-      const response = yield call(queryCompany, payload)
-      if (response && response.code === consts.RET_CODE.SUCCESS) {
+    *fetchTags({ payload }, { call, put }) {
+      const response = yield call(queryTagList, payload)
+      if (response?.code === consts.RET_CODE.SUCCESS) {
+        const { tagType } = payload
         yield put({
-          type: 'show',
-          payload: response.data
+          type: 'changeState',
+          payload: {
+            key: tagType === TagTypeEnum.PERSONTAG ? 'personTags' : 'teamTags',
+            data: response.data
+          }
+        })
+        yield put({
+          type: 'changeState',
+          payload: {
+            key: tagType === TagTypeEnum.PERSONTAG ? 'personTagColorMap' : 'teamTagColorMap',
+            data: response.data.reduce((curr, prev, idx) => {
+              const item = { ...curr, [prev.id]: LabelStatusColorMap[tagType][idx] }
+              return item
+            }, {})
+          }
         })
-
       }
-      // console.log(response)
     }
   },
-  // reducers:用于处理同步操作,由action触发,可修改state
   reducers: {
-    // action:是由reducers及effects的触发器,一般是第一个对象,如:{type:'add',payload:todo}
-    show(state, action) {
+    // 通用reducer
+    changeState(state, action) {
       return {
         ...state,
-        data: action.payload.customer,
-        total: action.payload.total
+        [action.payload.key]: action.payload.data
       }
     }
   }

+ 29 - 18
src/models/contact.js

@@ -1,38 +1,49 @@
 import consts from '@/consts'
-import { queryContact } from '@/services/contact'
+import { TagTypeEnum } from '@/pages/Customer/Company/components/PersonLabel'
+import { LabelStatusColorMap } from '@/pages/Customer/Company/components/PersonLabel'
+import { queryTagList } from '@/services/customer'
 
 export default {
   namespace: 'contact',
   state: {
-    data: [],
-    journal:[],
-    servicelist:[],
-    total: 0
+    personTags: [], // 个人标签
+    personTagColorMap: {}, // 个人标签的颜色映射关系
+    teamTags: [], // 协作标签
+    teamTagColorMap: {} // 协作标签的颜色映射关系
   },
   // 用于处理异步操作和业务逻辑,由action触发,但不能修改state
   effects: {
-    *fetch({ payload }, { call, put }) {
-      const response = yield call(queryContact, payload)
-      if (response && response.code === consts.RET_CODE.SUCCESS) {
+    *fetchTags({ payload }, { call, put }) {
+      const response = yield call(queryTagList, payload)
+      if (response?.code === consts.RET_CODE.SUCCESS) {
+        const { tagType } = payload
         yield put({
-          type: 'show',
-          payload: response.data
+          type: 'changeState',
+          payload: {
+            key: tagType === TagTypeEnum.PERSONTAG ? 'personTags' : 'teamTags',
+            data: response.data
+          }
+        })
+        yield put({
+          type: 'changeState',
+          payload: {
+            key: tagType === TagTypeEnum.PERSONTAG ? 'personTagColorMap' : 'teamTagColorMap',
+            data: response.data.reduce((curr, prev, idx) => {
+              const item = { ...curr, [prev.id]: LabelStatusColorMap[tagType][idx] }
+              return item
+            }, {})
+          }
         })
-
       }
-      // console.log(response)
     }
   },
   // reducers:用于处理同步操作,由action触发,可修改state
   reducers: {
-    // action:是由reducers及effects的触发器,一般是第一个对象,如:{type:'add',payload:todo}
-    show(state, action) {
+    // 通用reducer
+    changeState(state, action) {
       return {
         ...state,
-        data: action.payload.client,
-        journal: action.payload.log,
-        servicelist:action.payload.serviceLog,
-        total: action.payload.total
+        [action.payload.key]: action.payload.data
       }
     }
   }

+ 1 - 32
src/models/customer.js

@@ -1,17 +1,10 @@
 import consts from '@/consts'
-import { TagTypeEnum } from '@/pages/Customer/Company/components/PersonLabel'
-import { LabelStatusColorMap } from '@/pages/Customer/Company/components/PersonLabel'
 import { queryNatures } from '@/services/contact'
-import { queryTagList } from '@/services/customer'
 
 export default {
   namespace: 'customer',
   state: {
-    natures: [], // 客户性质列表
-    personTags: [], // 个人标签
-    personTagColorMap: {}, // 个人标签的颜色映射关系
-    teamTags: [], // 协作标签
-    teamTagColorMap: {} // 协作标签的颜色映射关系
+    natures: [] // 客户性质列表
   },
   effects: {
     *fetch({ payload }, { call, put }) {
@@ -25,30 +18,6 @@ export default {
           }
         })
       }
-    },
-    *fetchTags({ payload }, { call, put }) {
-      console.log('payload', payload)
-      const response = yield call(queryTagList, payload)
-      if (response?.code === consts.RET_CODE.SUCCESS) {
-        const { tagType } = payload
-        yield put({
-          type: 'changeState',
-          payload: {
-            key: tagType === TagTypeEnum.PERSONTAG ? 'personTags' : 'teamTags',
-            data: response.data
-          }
-        })
-        yield put({
-          type: 'changeState',
-          payload: {
-            key: tagType === TagTypeEnum.PERSONTAG ? 'personTagColorMap' : 'teamTagColorMap',
-            data: response.data.reduce((curr, prev, idx) => {
-              const item = { ...curr, [prev.id]: LabelStatusColorMap[tagType][idx] }
-              return item
-            }, {})
-          }
-        })
-      }
     }
   },
   reducers: {

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

@@ -55,7 +55,8 @@ const PersonLabel = props => {
     tagColumn,
     modeType = 'column',
     checkCallBack,
-    personTags,
+    contactPersonTags,
+    companyPersonTags,
     dispatch
   } = props
   const [dropdownType, setDropdownType] = useState(DropdownTypeEnum.check)
@@ -64,7 +65,7 @@ const PersonLabel = props => {
   const [showOkBtn, setShowOkBtn] = useState(false)
   const initData = () => {
     dispatch({
-      type: 'customer/fetchTags',
+      type: `${tagColumn === TagDataTypeEnum.COMPANY ? 'contact' : 'company'}/fetchTags`,
       payload: { tagType, tagColumn }
     })
   }
@@ -89,7 +90,7 @@ const PersonLabel = props => {
         checkedIds={checkedIds}
         setCheckedIds={setCheckedIds}
         dataId={dataId}
-        personTags={personTags}
+        personTags={tagColumn === TagDataTypeEnum.CONTACT ? contactPersonTags : companyPersonTags}
         menuType={dropdownType}
         tagType={tagType}
         tagColumn={tagColumn}
@@ -261,7 +262,7 @@ const DropDownMenu = props => {
   )
 }
 
-export default connect(({ customer }) => ({
-  personTags: customer.personTags,
-  personTagColorMap: customer.personTagColorMap
+export default connect(({ contact, company }) => ({
+  contactPersonTags: contact.personTags,
+  companyPersonTags: company.personTags
 }))(PersonLabel)

+ 158 - 12
src/pages/Customer/Company/index.jsx

@@ -1,7 +1,8 @@
 import React, { useState, useEffect } from 'react'
 import ProTable from '@ant-design/pro-table'
-import useAutoTable from '@/hooks/useAutoTable'
-import { message } from 'antd'
+import useAutoTable, { useTableScroll } from '@/hooks/useAutoTable'
+import { Button, message } from 'antd'
+import { connect } from 'dva'
 import consts from '@/consts'
 import { queryCompany, apiAddCompany } from '@/services/company'
 import LazyCascader from '@/components/LazyCascader'
@@ -9,13 +10,20 @@ import CompanyDetail from '@/pages/Customer/Company/components/CompanyDetail'
 import AddCompany from '@/pages/Customer/Company/components/AddCompany'
 import { useModal } from '@/components/Modal'
 import { formatValues } from '@/components/LazyCascader'
+import PersonLabel from './components/PersonLabel'
+import { TagTypeEnum } from './components/PersonLabel'
+import { TagDataTypeEnum } from './components/PersonLabel'
+import { LabelModeType } from './components/PersonLabel'
+import { IconPark } from '@/components/SvgIcon'
+import { apiBatchLink } from '@/services/customer'
 
-const Company = () => {
+const Company = props => {
+  const { personTagColorMap, teamTagColorMap, personTags, teamTags, dispatch } = props
   const { toggleDrawer, setDrawerProps } = useModal()
-  const needSubtractHeight = 48 + 48 + 48 + 64 + 24 + 32 // 需要被裁掉的高度
-  const needSubtractWidth = 48 + 48 + 48 // 需要被裁掉的高度
+  // const needSubtractHeight = 48 + 48 + 48 + 64 + 24 + 32 // 需要被裁掉的高度
+  // const needSubtractWidth = 48 + 48 + 48 // 需要被裁掉的高度
 
-  const [x, y] = useAutoTable(needSubtractHeight, needSubtractWidth)
+  // const [x, y] = useAutoTable(needSubtractHeight, needSubtractWidth)
 
   const [state, setState] = useState({
     data: [],
@@ -51,6 +59,18 @@ const Company = () => {
 
   useEffect(() => {
     initData()
+    if (!personTags.length) {
+      dispatch({
+        type: 'company/fetchTags',
+        payload: { tagType: TagTypeEnum.PERSONTAG, tagColumn: TagDataTypeEnum.COMPANY }
+      })
+    }
+    if (!teamTags.length) {
+      dispatch({
+        type: 'company/fetchTags',
+        payload: { tagType: TagTypeEnum.TEAMTAG, tagColumn: TagDataTypeEnum.COMPANY }
+      })
+    }
   }, [])
   const handleSearch = value => {
     initData({ search: value })
@@ -79,20 +99,40 @@ const Company = () => {
       title: '客户名称',
       dataIndex: 'companyName',
       key: 'companyName',
-      width: 320,
+      width: 150,
       render: (companyName, record) => <a onClick={() => showDrawer(record.id)}>{companyName}</a>
     },
     {
       title: '地区',
       dataIndex: 'local',
       key: 'local',
-      width: 150
+      width: 100
     },
     {
       title: '个人标签',
       dataIndex: 'biaoqian',
       key: 'biaoqian',
-      width: 80
+      width: 100,
+      render: (_, record) => (
+        <div className="zh-align-center zh-flex-wrap">
+          {record.tagIds.map(item => (
+            <span
+              key={record.id + item}
+              className="zh-circle-icon zh-mg-right-3"
+              style={{ backgroundColor: personTagColorMap[item] }}
+            />
+          ))}
+          <PersonLabel
+            dataId={record.id}
+            tagIds={record.tagIds}
+            tagType={TagTypeEnum.PERSONTAG}
+            tagColumn={TagDataTypeEnum.COMPANY}
+            checkCallBack={initData}
+            modeType={LabelModeType.column}>
+            <IconPark type="add" theme="filled" size="20" fill="#868e96" className="zh-pointer" />
+          </PersonLabel>
+        </div>
+      )
     },
     {
       title: '协作标签',
@@ -182,8 +222,45 @@ const Company = () => {
     }
   })
 
+  const [selectKeys, setSelectKeys] = useState([])
+  const hanleRowSelectChange = selectedRowKeys => {
+    setSelectKeys(selectedRowKeys)
+  }
+
+  const [tagState, setTagState] = useState({
+    tagIds: [],
+    tagCooperationIds: []
+  })
+
+  const toolbarOptionsChange = (type, ids) => {
+    setTagState({ ...tagState, [type]: ids })
+  }
+
+  const { getScrollRef } = useTableScroll(columns, selectKeys)
+
+  // 批量设置-确认触发
+  const tagsSettingConfirm = async clientIds => {
+    if (tagState.tagIds.length) {
+      await apiBatchLink({
+        tagIds: tagState.tagIds,
+        dataIds: clientIds,
+        dataType: TagDataTypeEnum.COMPANY,
+        tagType: TagTypeEnum.PERSONTAG
+      })
+    }
+    if (tagState.tagCooperationIds.length) {
+      await apiBatchLink({
+        tagIds: tagState.tagIds,
+        dataIds: clientIds,
+        dataType: TagDataTypeEnum.COMPANY,
+        tagType: TagTypeEnum.PERSONTAG
+      })
+    }
+    initData()
+  }
+
   return (
-    <div>
+    <div className="zh-container">
       <ProTable
         rowKey={record => record.id}
         search={false}
@@ -198,7 +275,71 @@ const Company = () => {
             <AddCompany onConfirm={handleAddCompany} key="addCompanyBtn" />
           ]
         }}
-        scroll={{ x, y }}
+        rowSelection={{
+          onChange: hanleRowSelectChange,
+          type: 'checkbox'
+        }}
+        tableAlertRender={({ selectedRowKeys }) => {
+          return (
+            <div className="zh-flex-row">
+              <div className="zh-mg-right-5">
+                <PersonLabel
+                  tagType={TagTypeEnum.PERSONTAG}
+                  tagColumn={TagDataTypeEnum.COMPANY}
+                  checkCallBack={toolbarOptionsChange}
+                  modeType={LabelModeType.toolbar}>
+                  {tagState.tagIds.length ? (
+                    <Button>
+                      {tagState.tagIds.map(item => (
+                        <span
+                          key={item}
+                          className="zh-circle-icon zh-mg-right-3"
+                          style={{ backgroundColor: personTagColorMap[item] }}
+                        />
+                      ))}
+                      <IconPark type="down" />
+                    </Button>
+                  ) : (
+                    <Button>
+                      个人标签
+                      <IconPark type="down" />
+                    </Button>
+                  )}
+                </PersonLabel>
+              </div>
+              <div className="zh-mg-right-5">
+                <PersonLabel
+                  tagType={TagTypeEnum.TEAMTAG}
+                  tagColumn={TagDataTypeEnum.COMPANY}
+                  checkCallBack={toolbarOptionsChange}
+                  modeType={LabelModeType.toolbar}>
+                  {tagState.tagCooperationIds.length ? (
+                    <Button>
+                      {tagState.tagCooperationIds.map(item => (
+                        <span
+                          key={item}
+                          className="zh-square-icon zh-mg-right-3"
+                          style={{ backgroundColor: teamTagColorMap[item] }}
+                        />
+                      ))}
+                      <IconPark type="down" />
+                    </Button>
+                  ) : (
+                    <Button>
+                      协作标签
+                      <IconPark type="down" />
+                    </Button>
+                  )}
+                </PersonLabel>
+              </div>
+              <Button onClick={() => tagsSettingConfirm(selectedRowKeys)}>
+                <IconPark type="check" size={14} />
+                确认
+              </Button>
+            </div>
+          )
+        }}
+        scroll={getScrollRef}
         columnsStateMap={columnsStateMap}
         onColumnsStateChange={map => setColumnsStateMap(map)}
         columns={columns}
@@ -213,4 +354,9 @@ const Company = () => {
   )
 }
 
-export default Company
+export default connect(({ company }) => ({
+  personTagColorMap: company.personTagColorMap,
+  personTags: company.personTags,
+  teamTagColorMap: company.teamTagColorMap,
+  teamTags: company.teamTags
+}))(Company)

+ 12 - 8
src/pages/Customer/Contact/index.jsx

@@ -42,11 +42,13 @@ const Contact = props => {
     servicelog: [],
     total: 0,
     current: 1,
+    loading: false,
     id: '',
     tail: { department: 0, email: 0, telephone: 0 }
   })
 
   const initData = async payload => {
+    setState({ ...state, loading: true })
     const {
       code = -1,
       data: { client = [], log = [], serviceLog = [], total = 0, tail } = {
@@ -65,6 +67,7 @@ const Contact = props => {
         servicelist: serviceLog,
         total,
         tail,
+        loading: false,
         current: payload?.page || 1
       })
     }
@@ -169,7 +172,7 @@ const Contact = props => {
         <div className="zh-align-center zh-flex-wrap">
           {record.tagIds.map(item => (
             <span
-              key={record.id + item.id}
+              key={record.id + item}
               className="zh-circle-icon zh-mg-right-3"
               style={{ backgroundColor: personTagColorMap[item] }}
             />
@@ -375,13 +378,13 @@ const Contact = props => {
     initData()
     if (!personTags.length) {
       dispatch({
-        type: 'customer/fetchTags',
+        type: 'contact/fetchTags',
         payload: { tagType: TagTypeEnum.PERSONTAG, tagColumn: TagDataTypeEnum.CONTACT }
       })
     }
     if (!teamTags.length) {
       dispatch({
-        type: 'customer/fetchTags',
+        type: 'contact/fetchTags',
         payload: { tagType: TagTypeEnum.TEAMTAG, tagColumn: TagDataTypeEnum.CONTACT }
       })
     }
@@ -425,6 +428,7 @@ const Contact = props => {
 
   return (
     <ProTable
+      loading={state.loading}
       rowKey={record => record.id}
       bordered
       columns={columns}
@@ -534,9 +538,9 @@ const Contact = props => {
   )
 }
 
-export default connect(({ customer }) => ({
-  personTagColorMap: customer.personTagColorMap,
-  personTags: customer.personTags,
-  teamTagColorMap: customer.teamTagColorMap,
-  teamTags: customer.teamTags
+export default connect(({ contact }) => ({
+  personTagColorMap: contact.personTagColorMap,
+  personTags: contact.personTags,
+  teamTagColorMap: contact.teamTagColorMap,
+  teamTags: contact.teamTags
 }))(Contact)