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

Merge branch 'master' of http://192.168.1.41:3000/outaozhen/cldV2react

outaozhen пре 5 година
родитељ
комит
a821a0f8a1

+ 15 - 2
src/models/customer.js

@@ -1,4 +1,5 @@
 import consts from '@/consts'
+import { LabelStatusColorMap } from '@/pages/Customer/Company/components/PersonLabel'
 import { queryNatures } from '@/services/contact'
 import { queryTagList } from '@/services/customer'
 
@@ -7,7 +8,9 @@ export default {
   state: {
     natures: [], // 客户性质列表
     personTags: [], // 个人标签
-    teamTags: [] // 协作标签
+    personTagColorMap: {}, // 个人标签的颜色映射关系
+    teamTags: [], // 协作标签
+    teamTagColorMap: {} // 协作标签的颜色映射关系
   },
   effects: {
     *fetch({ payload }, { call, put }) {
@@ -26,6 +29,7 @@ export default {
       const response = yield call(queryTagList, payload)
       if (response?.code === consts.RET_CODE.SUCCESS) {
         const { tagType } = payload
+
         yield put({
           type: 'changeState',
           payload: {
@@ -33,13 +37,22 @@ export default {
             data: response.data
           }
         })
+        yield put({
+          type: 'changeState',
+          payload: {
+            key: tagType === 0 ? 'personTagColorMap' : 'teamTagColorMap',
+            data: response.data.reduce((curr, prev, idx) => {
+              const item = { ...curr, [prev.id]: LabelStatusColorMap[tagType][idx] }
+              return item
+            }, {})
+          }
+        })
       }
     }
   },
   reducers: {
     // 通用reducer
     changeState(state, action) {
-      console.log('action', action)
       return {
         ...state,
         [action.payload.key]: action.payload.data

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

@@ -1,8 +1,9 @@
-import { Button, Checkbox, Popover, Input } from 'antd'
-import React, { useState, useRef } from 'react'
+import { Button, Checkbox, Popover, Input, message, Spin } from 'antd'
+import React, { useState, useRef, useEffect } from 'react'
 import { connect } from 'dva'
 import styles from './index.scss'
-import { apiConnOrDelTag } from '@/services/customer'
+import { apiConnOrDelTag, apiUpdateTag } from '@/services/customer'
+import consts from '@/basic/consts'
 
 const DropdownTypeEnum = {
   check: 0,
@@ -22,10 +23,25 @@ export const LabelModeType = {
   column: 0, // 列操作
   toolbar: 1 // 常用于表格批量操作
 }
-
-export const LabelChangeType = {
-  conn: 0, // 关联标签
-  del: 1 // 删除标签
+export const LabelStatusColorMap = {
+  [TagTypeEnum.PERSONTAG]: [
+    '#16A085',
+    '#2980B9',
+    '#8E44AD',
+    '#f90000',
+    '#B8651B',
+    '#2C3E50',
+    '#efd200'
+  ],
+  [TagTypeEnum.TEAMTAG]: [
+    '#F8AC59',
+    '#FF69B4',
+    '#999999',
+    '#7186ab',
+    '#778b72',
+    '#c292ca',
+    '#a14751'
+  ]
 }
 
 const PersonLabel = props => {
@@ -42,16 +58,46 @@ const PersonLabel = props => {
   } = props
   const [dropdownType, setDropdownType] = useState(DropdownTypeEnum.check)
 
-  if (!personTags?.length) {
+  const [checkedIds, setCheckedIds] = useState(tagIds)
+  const [showOkBtn, setShowOkBtn] = useState(false)
+  const initData = () => {
     dispatch({
       type: 'customer/fetchTags',
       payload: { tagType, tagColumn }
     })
   }
 
+  // 监听勾选的标签的变化,实时显示确认按钮
+  useEffect(() => {
+    // 只对列操作起效
+    if (modeType === LabelModeType.column) {
+      setShowOkBtn(true)
+    }
+  }, [checkedIds])
+  // 列操作->更新个人标签勾选情况
+  const handleOnConfirm = async () => {
+    const payload = {
+      tagIds: checkedIds,
+      dataId,
+      dataType: tagColumn,
+      tagType
+    }
+    const { code = -1 } = await apiConnOrDelTag(payload)
+    if (code === consts.RET_CODE.SUCCESS) {
+      setShowOkBtn(false)
+    }
+  }
+  useEffect(() => {
+    if (!personTags?.length) {
+      initData()
+    }
+  }, [])
+
   const menu = (
     <div className="zh-mg-tb-7 zh-mg-lf-14">
       <DropDownMenu
+        checkedIds={checkedIds}
+        setCheckedIds={setCheckedIds}
         dataId={dataId}
         personTags={personTags}
         tagIds={tagIds}
@@ -60,23 +106,23 @@ const PersonLabel = props => {
         tagColumn={tagColumn}
         modeType={modeType}
         checkCallBack={checkCallBack}
+        initCallBack={initData}
       />
       <div className="zh-mg-top-5">
         {dropdownType === DropdownTypeEnum.check ? (
-          <span
-            onClick={() => setDropdownType(DropdownTypeEnum.edit)}
-            className="zh-primary zh-pointer">
-            编辑标签
-          </span>
+          <div className="zh-justify-between">
+            <span
+              onClick={() => setDropdownType(DropdownTypeEnum.edit)}
+              className="zh-primary zh-pointer">
+              编辑标签
+            </span>
+            {modeType === LabelModeType.column && showOkBtn ? (
+              <Button onClick={() => handleOnConfirm()}>确认</Button>
+            ) : null}
+          </div>
         ) : (
           <>
-            <Button type="primary">确认</Button>
-            <Button
-              type="default"
-              className="zh-mg-left-5"
-              onClick={() => setDropdownType(DropdownTypeEnum.check)}>
-              取消
-            </Button>
+            <Button onClick={() => setDropdownType(DropdownTypeEnum.check)}>返回</Button>
           </>
         )}
       </div>
@@ -93,56 +139,58 @@ const DropDownMenu = props => {
   // 保存选中的checkbox ids
   const {
     tagIds,
+    checkedIds,
+    setCheckedIds,
     dataId,
     menuType,
     tagType,
     tagColumn,
     personTags,
     modeType,
-    checkCallBack
+    checkCallBack,
+    initCallBack
   } = props
-  const [checkedIds, setCheckedIds] = useState(tagIds)
 
-  const [activeId, setActiveId] = useState('')
+  const [activeState, setActiveState] = useState({
+    id: '',
+    activeId: '',
+    loading: false
+  })
   const inputRef = useRef(null)
-  const statusColors = {
-    [TagTypeEnum.PERSONTAG]: [
-      '#16A085',
-      '#2980B9',
-      '#8E44AD',
-      '#f90000',
-      '#B8651B',
-      '#2C3E50',
-      '#efd200'
-    ],
-    [TagTypeEnum.TEAMTAG]: [
-      '#F8AC59',
-      '#FF69B4',
-      '#999999',
-      '#7186ab',
-      '#778b72',
-      '#c292ca',
-      '#a14751'
-    ]
-  }
 
   const handleOnchange = async (e, id) => {
     const { checked } = e.target
-    if (modeType === LabelModeType.column) {
-      const payload = {
-        type: checked ? LabelChangeType.conn : LabelChangeType.del,
-        data: { tagId: id, dataId, dataType: tagColumn, tagType }
-      }
-      const { code = -1 } = await apiConnOrDelTag(payload)
-    } else {
-      const ids = checked ? [...checkedIds, id] : checkedIds.filter(item => item)
-      setCheckedIds(ids)
+    const ids = checked ? [...checkedIds, id] : checkedIds.filter(item => item)
+    setCheckedIds(ids)
+    // 如果是toolbar的操作则要触发对应
+    if (modeType === LabelModeType.toolbar) {
       checkCallBack && checkCallBack(ids)
     }
   }
 
   const labelOnClick = id => {
-    setActiveId(id)
+    setActiveState({ ...activeState, id, activeId: id })
+  }
+  useEffect(() => {
+    activeState.id && inputRef.current?.focus()
+  }, [activeState.id])
+
+  const labelTextChange = async (e, oldVal) => {
+    const { value: newVal = '' } = e.currentTarget
+    if (newVal && oldVal !== newVal) {
+      const { code = -1 } = await apiUpdateTag({ name: newVal, id: activeState.id })
+      if (code === consts.RET_CODE.SUCCESS) {
+        setActiveState({ ...activeState, loading: true })
+        initCallBack()
+        const delayUpdate = () => {
+          setTimeout(() => {
+            setActiveState({ ...activeState, activeId: '', loading: false })
+            message.success('标签已更新')
+          }, 500)
+        }
+        await delayUpdate()
+      }
+    }
   }
   return (
     <ul className={styles.labelContent}>
@@ -155,7 +203,7 @@ const DropDownMenu = props => {
                     styles.statusIcon,
                     tagType === TagTypeEnum.PERSONTAG ? styles.suqare : ''
                   ].join(' ')}
-                  style={{ backgroundColor: statusColors[tagType][idx] }}
+                  style={{ backgroundColor: LabelStatusColorMap[tagType][idx] }}
                 />
                 <span>{item.name}</span>
               </div>
@@ -170,26 +218,39 @@ const DropDownMenu = props => {
         : null}
       {menuType === DropdownTypeEnum.edit
         ? personTags.map((item, idx) => (
-            <li
+            <Spin
               key={item.id}
-              className={[
+              spinning={item.id === activeState.id ? activeState.loading : false}
+              wrapperClassName={[
                 styles.menuItem,
                 styles.editMenuItem,
-                item.id === activeId ? styles.active : ''
-              ].join(' ')}
-              onClick={() => labelOnClick(item.id)}>
-              <div className={styles.editLabel}>
-                <div
-                  className={[
-                    styles.statusIcon,
-                    tagType === TagTypeEnum.PERSONTAG ? styles.suqare : ''
-                  ].join(' ')}
-                  style={{ backgroundColor: statusColors[tagType][idx] }}
-                />
+                item.id === activeState.activeId ? styles.active : ''
+              ].join(' ')}>
+              <li onClick={() => labelOnClick(item.id)} style={{ width: '100%' }}>
+                <div className={styles.editLabel}>
+                  <div
+                    className={[
+                      styles.statusIcon,
+                      tagType === TagTypeEnum.PERSONTAG ? styles.suqare : ''
+                    ].join(' ')}
+                    style={{ backgroundColor: statusColors[tagType][idx] }}
+                  />
 
-                {activeId === item.id ? <Input ref={inputRef} /> : <span>{item.name}</span>}
-              </div>
-            </li>
+                  {activeState.activeId === item.id ? (
+                    <Input
+                      ref={inputRef}
+                      size="small"
+                      bordered={false}
+                      defaultValue={item.name}
+                      onBlur={e => labelTextChange(e, item.name)}
+                      onPressEnter={e => labelTextChange(e, item.name)}
+                    />
+                  ) : (
+                    <span>{item.name}</span>
+                  )}
+                </div>
+              </li>
+            </Spin>
           ))
         : null}
     </ul>
@@ -197,5 +258,6 @@ const DropDownMenu = props => {
 }
 
 export default connect(({ customer }) => ({
-  personTags: customer.personTags
+  personTags: customer.personTags,
+  personTagColorMap: customer.personTagColorMap
 }))(PersonLabel)

+ 7 - 1
src/pages/Customer/Company/components/PersonLabel/index.scss

@@ -2,11 +2,14 @@
   margin-bottom: 0;
   padding: 0;
   list-style: none;
+  :global(.ant-spin-container) {
+    width: 100%;
+  }
   .menuItem {
     display: flex;
     flex-direction: row;
     flex-wrap: nowrap;
-    min-width: 200px;
+    min-width: 212px;
     margin-bottom: 0.25rem;
     padding: 0;
     color: #666666;
@@ -32,6 +35,9 @@
       display: flex;
       align-items: center;
       padding: 8px 14px;
+      :global(.ant-input-sm) {
+        padding: 0;
+      }
     }
     .statusIcon {
       width: 12px;

+ 40 - 13
src/pages/Customer/Contact/index.jsx

@@ -13,13 +13,18 @@ import LazyCascader from '@/components/LazyCascader'
 import { useDebounceFn } from 'ahooks'
 import { useModal } from '@/components/Modal'
 import { formatValues } from '@/components/LazyCascader'
+import PersonLabel from '../Company/components/PersonLabel'
+import { TagTypeEnum } from '../Company/components/PersonLabel'
+import { TagDataTypeEnum } from '../Company/components/PersonLabel'
+import { IconPark } from '@/components/SvgIcon'
 
-const Contact = () => {
+const Contact = props => {
   const { toggleDrawer, setDrawerProps } = useModal()
-  const needSubtractHeight = 48 + 48 + 48 + 64 + 24 + 32 // 需要被裁掉的高度
-  const needSubtractWidth = 48 + 48 + 48 // 需要被裁掉的高度
+  const { personTagColorMap, teamTagColorMap } = props
+  // 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 [selectKeys, setSelectKeys] = useState([])
   const hanleRowSelectChange = selectedRowKeys => {
@@ -31,6 +36,7 @@ const Contact = () => {
     journal: [],
     servicelog: [],
     total: 0,
+    current: 1,
     id: '',
     tail: { department: 0, email: 0, telephone: 0 }
   })
@@ -47,7 +53,15 @@ const Contact = () => {
       }
     } = await queryContact(payload)
     if (code === consts.RET_CODE.SUCCESS) {
-      setState({ ...state, data: client, journal: log, servicelist: serviceLog, total, tail })
+      setState({
+        ...state,
+        data: client,
+        journal: log,
+        servicelist: serviceLog,
+        total,
+        tail,
+        current: payload?.page || 1
+      })
     }
   }
 
@@ -147,7 +161,23 @@ const Contact = () => {
       dataIndex: 'tag',
       key: 'tag',
       width: 100,
-      render: (_, record) => <Tag color="#16A085">{record.tag}</Tag>,
+      render: (_, record) => (
+        <div>
+          {record.tagIds.map((item, idx) => (
+            <Tag key={record.id + item.id} color={personTagColorMap[item]}>
+              {record.tagName[idx]}
+            </Tag>
+          ))}
+          <PersonLabel
+            dataId={record.id}
+            tagIds={record.tagIds}
+            tagType={TagTypeEnum.PERSONTAG}
+            tagColumn={TagDataTypeEnum.CONTACT}
+            checkCallBack={() => initData()}>
+            <IconPark type="add" fill="#868e96" size={14} />
+          </PersonLabel>
+        </div>
+      ),
       filters: true,
       // onFilter: true,
       valueType: 'select',
@@ -182,9 +212,6 @@ const Contact = () => {
           status: 'yellow'
         }
       }
-      // render:(_, record) => {
-      //   console.log(record);
-      // }
     },
     {
       title: '协作标签',
@@ -334,7 +361,6 @@ const Contact = () => {
   })
 
   const { getScrollRef } = useTableScroll(columns, selectKeys)
-  console.log(getScrollRef)
 
   // 默认刷新一次列表请求数据
 
@@ -375,6 +401,7 @@ const Contact = () => {
       pagination={{
         pageSize: consts.PAGE_SIZE,
         total: state.total,
+        current: state.current,
         onChange: (page, size) => initData({ page, size })
       }}
       tableAlertRender={({ selectedRowKeys }) => {
@@ -410,7 +437,7 @@ const Contact = () => {
   )
 }
 
-export default connect(({ district, loading }) => ({
-  districtName: district,
-  getlist: loading.effects['contact/fetch']
+export default connect(({ customer }) => ({
+  personTagColorMap: customer.personTagColorMap,
+  teamTagColorMap: customer.teamTagColorMap
 }))(Contact)

+ 14 - 10
src/services/customer.js

@@ -37,18 +37,22 @@ export async function queryTagList(params) {
 }
 
 /**
- * 标签关联/删除
+ * 标签关联/批量更新/替换更新
  * @param {*} type
  * @param {*} payload
  * @returns
  */
-export async function apiConnOrDelTag({ type, data }) {
-  let url = ''
-  if (type === LabelChangeType.conn) {
-    url = '/api/tag/link'
-  } else {
-    url = '/api/tag/link/delete'
-  }
-  const res = await request(url, { data })
-  return res
+export async function apiConnOrDelTag(payload) {
+  const data = await request.post('/api/tag/link', { data: payload })
+  return data
+}
+
+/**
+ * 编辑标签
+ * @param {*} payload
+ * @returns
+ */
+export async function apiUpdateTag(payload) {
+  const data = await request.post('/api/tag/update', { data: payload })
+  return data
 }