Browse Source

refactor: MoneyInput组件重构

lanjianrong 4 years ago
parent
commit
ec74c66301

+ 74 - 42
src/components/MoneyInput/index.tsx

@@ -1,74 +1,106 @@
 import { Form } from 'antd'
 import Input, { InputProps } from 'antd/lib/input'
-import React, { ChangeEvent, useState } from 'react'
+import React, { ChangeEvent, useState, useEffect } from 'react'
 import './index.scss'
 
-interface iState {
-  digit: string
-  inputVal: string
-}
+// interface iState {
+//   digit: string
+//   inputVal: string
+// }
 
-interface MoneyInputProps extends InputProps {
+interface MoneyInputProps  {
   maxPrice?: number
-  setValue: (val: string | null) => void
+  minPrice?: number
+  value?: string
+  digitLimit?: number
+  onChange?: (value: string) => void
+  // setValue: (val: string | null) => void
 }
 const MoneyInput: React.FC<MoneyInputProps> = (props) => {
-  const { maxPrice, setValue, ...restProps } = props
-  const [ state, setState ] = useState<iState>({
-    digit: '',
-    inputVal: ''
-  })
+  const { minPrice, maxPrice, value, onChange, digitLimit = 2 } = props
+
+  // const reg = new RegExp("^(\d*)\.?(\d{0," + digitLimit + "})")
+  const [ inputVal, setInputVal ] = useState('')
+
+  const [ digit, setDigit ] = useState<number | null>(null)
+
+  const triggerChange = (changedValue: string) => {
+    onChange && onChange(changedValue)
+  }
+
+  const digits = {
+    4: '千',
+    5: '万',
+    6: '十万',
+    7: '百万',
+    8: '千万',
+    9: '亿',
+    10: '十亿',
+    11: '百亿',
+    12: '千亿'
+  }
+
+  useEffect(() => {
+    if (value) {
+      const len = value.match(/^(\d*)\.?(\d{0,2})/)
+      len && setDigit(digits[len[1].length])
+    }
+  }, [ value ])
 
 
+  const changeDigitMap = (len: number | null) => {
+    if (len && digits[len]) {
+      setDigit(digits[len])
+    } else {
+      setDigit(null)
+    }
+  }
   const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
-    const val = e.target.value?.match(/^\d+/)
-    let inputVal = val && val[0] || null
-    if (inputVal) {
-      if (maxPrice && parseFloat(inputVal) > maxPrice) {
-        inputVal = maxPrice.toString()
-      }
+    const val = e.target.value?.match(/^(\d*)\.?(\d{0,2})/)
+    const newInputVal = val && val[0] || ''
 
-      let len = inputVal.length
+    if (newInputVal) {
+      // if (maxPrice && parseFloat(newInputVal) > maxPrice) {
+      //   newInputVal = maxPrice.toString()
+      // }
 
-      if (len > 12) {
-        inputVal = state.inputVal
-        len = state.inputVal.length
-      }
-      const digits = {
-        4: '千',
-        5: '万',
-        6: '十万',
-        7: '百万',
-        8: '千万',
-        9: '亿',
-        10: '十亿',
-        11: '百亿',
-        12: '千亿'
+      // if (minPrice && parseFloat(newInputVal) < minPrice) {
+      //   console.log(minPrice)
+
+      //   newInputVal = minPrice.toString()
+      // }
+      const len = val && val[1].length || null
+
+      if (len && len > 12) {
+        return
       }
-      if(digits[len]) {
-        setState({ ...state, digit: digits[len], inputVal })
+      if (len && digits[len]) {
+        changeDigitMap(len)
       } else {
-        setState({ ...state, digit: '', inputVal })
+        changeDigitMap(null)
       }
     }
 
-    setValue(inputVal)
+    if (!newInputVal) {
+      setInputVal('')
+    }
+    triggerChange(newInputVal)
   }
 
   return (
     <div className="ant-input ant-input-sm">
       <div className="pi-flex-row">
       <span className="pi-fz-14 pi-mg-right-5">¥</span>
-      <Form.Item name="price" rules={[ { required: true, message: '请输入合同金额' } ]} noStyle>
-        <Input bordered={false} {...restProps} onChange={handleChange} autoComplete="off"/>
-      </Form.Item>
+      {/* <Form.Item name="price" rules={[ { required: true, message } ]} noStyle> */}
+        <Input bordered={false} value={inputVal || value} size="small" onChange={handleChange} autoComplete="off"/>
+      {/* </Form.Item> */}
       </div>
       {
-        state.digit ?
+        digit ?
         <div className='line-separator'>
           <div className="side-line"> </div>
           <div className="triangle-triangle" />
-          <div className="triangle-num">{state.digit}</div>
+          <div className="triangle-num">{digit}</div>
           <div className="side-line"> </div>
         </div>
         : null

+ 26 - 13
src/pages/Contract/Content/Income/components/Modal/index.tsx

@@ -7,11 +7,11 @@ import { iModalCommonProps } from '@/types/contract'
 import { apiContractSection } from '@/utils/common/api'
 import { contractTreeBaseId } from '@/utils/common/constStatus'
 import consts from '@/utils/consts'
-import { dayjsFormat, handleAutoCode } from '@/utils/util'
-import { Button, Form, Input, Modal, Select, Tree, TreeSelect } from 'antd'
+import { dayjsFormat, formatMoney, handleAutoCode } from '@/utils/util'
+import { Button, Form, Input, Modal, Select, TreeSelect } from 'antd'
 import locale from 'antd/es/date-picker/locale/zh_CN'
 import dayjs from 'dayjs'
-import React, { useEffect, useState } from 'react'
+import React, { useEffect, useState, useMemo } from 'react'
 import { apiGetReturnWay } from '../Tabs/Receivable/api'
 import styles from './index.module.scss'
 interface ContractSection {
@@ -123,12 +123,10 @@ const ContractModal: React.FC<iModalCommonProps> = ({ modalObj: { type, visible,
   }
 
   // 处理添加回款的金额不应该超出最大值
-  const handleMaxPrice = () => {
-    const maxPrice = parseFloat(contractStore.contract.price) - parseFloat(contractStore.contract.returned)
-    return maxPrice
-  }
+  const maxPrice = parseFloat(contractStore.contract.price) - parseFloat(contractStore.contract.returned)
 
 
+  const minPrice = parseFloat(contractType === ContractType.INCOME ? row.contractReturned : row.contractsPaid)
   return (
     <Modal
       getContainer={false}
@@ -198,6 +196,7 @@ const ContractModal: React.FC<iModalCommonProps> = ({ modalObj: { type, visible,
           <>
             <Form.Item name="code" label="合同编号" rules={[ { required: true, message: '请输入合同编号' } ]} className={styles.contractFormItem}>
               <Input
+                autoComplete='off'
                 addonAfter={
                   <span className="pi-pd-lr-11" onClick={() => autoCode()}>
                     自动编号
@@ -213,9 +212,9 @@ const ContractModal: React.FC<iModalCommonProps> = ({ modalObj: { type, visible,
                 <Option value={2}>收入合同</Option>
               </Select>
             </Form.Item> */}
-            <Form.Item label="合同金额" >
+            <Form.Item label="合同金额" name="price" rules={[ { required: true, message: '请输入合同金额' } ]}>
               {/* <Input type="number" placeholder="输入合同金额" addonAfter={<span>元</span>} /> */}
-              <MoneyInput size="small" setValue={(val: string | null) => form.setFieldsValue({ price: val })}/>
+              <MoneyInput />
             </Form.Item>
           </>
         ) : (
@@ -229,8 +228,15 @@ const ContractModal: React.FC<iModalCommonProps> = ({ modalObj: { type, visible,
             <Form.Item name="name" label="合同名称" rules={[ { required: true, message: '请输入项目内容' } ]}>
               <Input />
             </Form.Item>
-            <Form.Item label="合同金额" >
-              <MoneyInput size="small" setValue={(val: string | null) => form.setFieldsValue({ price: val })}/>
+            <Form.Item label="合同金额" name="price" rules={[ { required: true, message: '请输入合同金额' }, () => ({
+              validator(_, value) {
+                if (value && parseFloat(value) < minPrice) {
+                  return Promise.reject(`当前金额不能低于${minPrice}`)
+                }
+                return Promise.resolve()
+              }
+            }) ]}>
+              <MoneyInput />
             </Form.Item>
             <Form.Item name="partyA" label="甲方" rules={[ { required: true, message: '请输入甲方' } ]}>
               <Input />
@@ -308,9 +314,16 @@ const ContractModal: React.FC<iModalCommonProps> = ({ modalObj: { type, visible,
             <Form.Item name="time" label={`${contractType === ContractType.INCOME ? '回款' : '支付'}日期`} rules={[ { required: true, message: '请选择回款日期' } ]}>
               <DatePicker allowClear locale={locale} className="pi-width-100P" />
             </Form.Item>
-            <Form.Item label={`${contractType === ContractType.INCOME ? '回款' : '支付'}金额`} >
+            <Form.Item name="price" label={`${contractType === ContractType.INCOME ? '回款' : '支付'}金额`} rules={[ { required: true, message: `请输入${contractType === ContractType.INCOME ? '回款' : '支付'}金额` }, () => ({
+              validator(_, value) {
+                if (value && parseFloat(value) > maxPrice) {
+                  return Promise.reject(`当前金额不能大于${minPrice}`)
+                }
+                return Promise.resolve()
+              }
+            }) ]}>
               {/* <Input onChange={handleReturnPrice}/> */}
-              <MoneyInput size="small" maxPrice={handleMaxPrice()} setValue={(val: string | null) => form.setFieldsValue({ price: val })}/>
+              <MoneyInput  />
             </Form.Item>
             <Form.Item name="way" label="支付方式" rules={[ { required: true, message: '请选择回款方式' } ]}>
               <Select>{options}</Select>

+ 0 - 26
src/pages/Contract/Content/Income/components/TableContent/index.tsx

@@ -37,7 +37,6 @@ interface iTableContentPorps {
 }
 
 const GCsheet: React.FC<iTableContentPorps> = ({ changeModalType, row, setRow, history, type }) => {
-  const [ expandedRowKeys, setRowKeys ] = useState<string[]>([])
 
   const [ sectionTemplate, setSectionTemplate ] = useState<iShowTemplateState>({
     isShow: false,
@@ -93,7 +92,6 @@ const GCsheet: React.FC<iTableContentPorps> = ({ changeModalType, row, setRow, h
         })
       } else {
         contractStore.updateTree(data.sectionTree.children)
-        setRowKeys(expandTree(data.sectionTree.children))
       }
     }
     // 初始化时如果id存在说明只是table更新了,那么要将store里面的合同数据也一起更新,防止合同详情不是最新的数据
@@ -282,7 +280,6 @@ const GCsheet: React.FC<iTableContentPorps> = ({ changeModalType, row, setRow, h
         editable: col.editable,
         dataIndex: col.dataIndex,
         title: col.title,
-        expandedRowKeys,
         handleSave
       })
     }
@@ -359,18 +356,8 @@ const GCsheet: React.FC<iTableContentPorps> = ({ changeModalType, row, setRow, h
               columns={_tableColumns as ColumnTypes}
               bordered
               pagination={false}
-              // rowKey={record => record.id}
               defaultExpandAllRows={true}
               onRow={onClickRow}
-              expandable={{ expandedRowKeys, onExpand: (expanded, record: any) => {
-                if (expanded) {
-                  const ids = [ ...expandedRowKeys, record.id ]
-                  setRowKeys(ids)
-                } else {
-                  const ids = expandedRowKeys.filter(item => item !== record.id)
-                  setRowKeys(ids)
-                }
-              } }}
               rowClassName={handleRowClass}
               style={{ height: '100%', overflowY: 'scroll' }}
             />
@@ -435,18 +422,5 @@ const GCsheet: React.FC<iTableContentPorps> = ({ changeModalType, row, setRow, h
     </div>
 }
 
-function expandTree(data: ContractTree[]): string[] {
-  let idArr: Array<string> = []
-
-  data.forEach((item: ContractTree) => {
-    if (item.children?.length) {
-      idArr.push(item.id)
-      const childIds = expandTree(item.children)
-      idArr = [ ...idArr, ...childIds ]
-    }
-  })
-  return idArr
-}
-
 
 export default observer(GCsheet)

+ 1 - 1
src/types/contract.d.ts

@@ -65,7 +65,7 @@ export interface iModalCommonProps {
   modalObj: iModalProps
   onConfirm: (values: any, type: string) => void
   onCancel: () => void
-  row: iIncomeTree
+  row: ContractTree
   reload: (type: string) => void
 }