Browse Source

feat: 收入合同 合同表格初始化

lanjianrong 4 năm trước cách đây
mục cha
commit
cf3d8b63c7

.prettierrc → .prettierrc.json


+ 11 - 11
.vscode/.settings.json

@@ -1,11 +1,11 @@
-{
-  "editor.tabSize": 2,
-  "path-intellisense.mappings": {
-    "@": "${workspaceRoot}"
-  },
-  // "typescript.suggest.paths": false,
-  "editor.formatOnSave": false,
-  "css.validate": false,
-  "scss.validate": false,
-  "typescript.validate.enable": false
-}
+// {
+//   "editor.tabSize": 2,
+//   "path-intellisense.mappings": {
+//     "@": "${workspaceRoot}"
+//   },
+//   // "typescript.suggest.paths": false,
+//   "editor.formatOnSave": false,
+//   "css.validate": false,
+//   "scss.validate": false,
+//   "typescript.validate.enable": false
+// }

+ 1 - 1
package.json

@@ -141,6 +141,7 @@
     "less": "^3.12.2",
     "less-loader": "^7.0.2",
     "mini-css-extract-plugin": "0.9.0",
+    "mobx-react-devtools": "^6.1.1",
     "node-sass": "^4.14.1",
     "optimize-css-assets-webpack-plugin": "5.0.3",
     "pnp-webpack-plugin": "1.6.4",
@@ -149,7 +150,6 @@
     "postcss-normalize": "8.0.1",
     "postcss-preset-env": "6.7.0",
     "postcss-safe-parser": "4.0.1",
-    "prettier": "^2.1.2",
     "react-app-polyfill": "^1.0.6",
     "react-dev-utils": "^10.2.1",
     "resolve": "1.15.0",

+ 12 - 8
src/App.tsx

@@ -1,22 +1,26 @@
 import Guards from '@/components/Navigation'
 import history from '@/utils/history'
+import { Provider } from 'mobx-react'
 import 'nprogress/nprogress.css'
 import React from 'react'
 import { AliveScope } from 'react-activation'
-// import { Provider } from 'react-keep-alive'
 import { Router, Switch } from 'react-router-dom'
 import './index.scss'
 import { routeConfig } from './router/routes'
+import * as stores from './store/mobx'
 const App = () =>{
   return (
     <div className="App">
-      <Router history={ history }>
-        <AliveScope>
-          <Switch>
-            <Guards routeConfig={routeConfig}></Guards>
-          </Switch>
-        </AliveScope>
-      </Router >
+      <Provider {...stores}>
+        <Router history={ history }>
+          <AliveScope>
+            <Switch>
+              <Guards routeConfig={routeConfig}></Guards>
+            </Switch>
+          </AliveScope>
+        </Router >
+
+      </Provider>
     </div>
   )
 }

+ 2 - 2
src/components/LeftSide/index.tsx

@@ -1,4 +1,4 @@
-import { tenderState } from '@/store/mobx'
+import { tenderStore } from '@/store/mobx'
 import { iNavSide } from '@/types/router'
 import React from 'react'
 import { Link } from 'react-router-dom'
@@ -6,7 +6,7 @@ import "./index.scss"
 
 const leftSide:React.FC<iNavSide> = ({ childRoutes, location }) => {
   if (location.state?.id) {
-    tenderState.saveBidsectionId(location.state?.id)
+    tenderStore.saveBidsectionId(location.state?.id)
   }
   const pathname = location.pathname
   return (

+ 4 - 4
src/components/Menu/index.tsx

@@ -1,5 +1,5 @@
 import logo from '@/assets/img/logo.png'
-import { userState } from '@/store/mobx'
+import { userStore } from '@/store/mobx'
 import { iMenuItem } from '@/types/router'
 import { Button, Dropdown, Menu } from "antd"
 import { observer } from 'mobx-react'
@@ -30,7 +30,7 @@ class NavSider extends Component<iMenuProps, any> {
             {
               MeunList?.map((item: iMenuItem, idx: number) => {
                 if (item.path === '/console/management') {
-                  return userState.role === 'ADMIN' ? !item.isTop && <MenuItem key={idx} item={item}></MenuItem> : ''
+                  return userStore.role === 'ADMIN' ? !item.isTop && <MenuItem key={idx} item={item}></MenuItem> : ''
                 } else {
                   return !item.isTop && <MenuItem key={idx} item={item}></MenuItem>
                 }
@@ -52,12 +52,12 @@ class NavSider extends Component<iMenuProps, any> {
                     </Menu.Item>
                     <Menu.Item key="3">
                       {/* <Link to="/login">退出登录</Link> */}
-                      <span onClick={() => userState.logout()}>退出登录</span>
+                      <span onClick={() => userStore.logout()}>退出登录</span>
                     </Menu.Item>
                   </Menu>
                 )
               }} trigger={[ 'click' ]} placement="topRight">
-                <Button size="small" className={styles.bottomBtn}>{userState.userInfo.name}</Button>
+                <Button size="small" className={styles.bottomBtn}>{userStore.userInfo.name}</Button>
               </Dropdown>
             </div>
           </div>

+ 7 - 7
src/components/Navigation/index.tsx

@@ -1,5 +1,5 @@
 import DefaultErrorPage from '@/pages/ErrorPage'
-import { frameState, userState } from '@/store/mobx'
+import { frameStore, userStore } from '@/store/mobx'
 import { NavigationGuardsProps, RouteModel } from '@/types/router'
 import { combinationPath } from '@/utils/util'
 import React, { Component } from 'react'
@@ -9,7 +9,7 @@ import { Redirect, Route } from "react-router-dom"
 class NavigationGuards extends Component<NavigationGuardsProps, any> {
     constructor(props: NavigationGuardsProps) {
         super(props)
-        userState.check()
+        userStore.check()
     }
     /**
      * 判断pathTarget是否包涵pathConfig/
@@ -65,8 +65,8 @@ class NavigationGuards extends Component<NavigationGuardsProps, any> {
         const targetPath: string | undefined = location?.pathname
 
         //如果访问子菜单,则跳转到子菜单的默认路由
-        if (targetPath && frameState.defaultRouteMapping.has(targetPath)) {
-            const targetDefaultRoute: string = frameState.defaultRouteMapping.get(targetPath) as string
+        if (targetPath && frameStore.defaultRouteMapping.has(targetPath)) {
+            const targetDefaultRoute: string = frameStore.defaultRouteMapping.get(targetPath) as string
             return <Redirect to={targetDefaultRoute}></Redirect>
         }
 
@@ -74,13 +74,13 @@ class NavigationGuards extends Component<NavigationGuardsProps, any> {
         const targetRoute: RouteModel | null | "" | undefined | Error = findRes && findRes.targetRoute
 
 
-        const isLogin = userState.isLogin
+        const isLogin = userStore.isLogin
 
         if (targetRoute instanceof Error) return <ErrorPage/>
 
         if (findRes) {
             const path: string = findRes.realPath ? findRes.realPath : ""
-            targetRoute && frameState.setCurrentRoutePath(path)
+            targetRoute && frameStore.setCurrentRoutePath(path)
         }
 
         if (!targetRoute) {
@@ -109,7 +109,7 @@ function LoginHandler(props: { targetRoute: RouteModel, ErrorPage: any }): any {
 
     if (path === '/login') {
         return <Redirect to="/console/dashboard"></Redirect>
-    } else if (!auth || NavigationGuards.permissionAuthentication(auth, userState.role)) {
+    } else if (!auth || NavigationGuards.permissionAuthentication(auth, userStore.role)) {
         return (
             <Route path={path} render={
                 props => (

+ 229 - 227
src/pages/Contract/Content/Income/GCsheet.tsx

@@ -1,232 +1,234 @@
-import { tenderState } from '@/store/mobx'
-import consts from '@/utils/consts'
-import GC from '@grapecity/spread-sheets'
-import { SpreadSheets, Worksheet } from '@grapecity/spread-sheets-react'
-import { Button, message, Radio, Table, Tabs } from 'antd'
-import Modal from 'antd/lib/modal/Modal'
-import { RadioChangeEvent } from 'antd/lib/radio'
-import React, { useEffect, useState } from 'react'
-import { useActivate } from 'react-activation'
-import { apiContractIncome, apiSetTemplate } from './components/Modal/api'
-import Detail from './components/Tabs/Detail'
-import File from './components/Tabs/File'
-import Receivable from './components/Tabs/Receivable'
-import styles from './index.module.scss'
-GC.Spread.Common.CultureManager.culture("zh-cn")
-// GC.Spread.Sheets.LicenseKey = secret
-interface iGCsheetPorps {
-  modalHandler: (type: string) => void
-}
-interface iTemplateState {
-  attribution: string
-  children: iTemplateState[] | undefined
-  depth: number
-  id: number
-  isEnd: boolean
-  leaf: boolean
-  name: string
-  parentId: number
-  serial: string
-}
-interface iShowTemplateState {
-  isShow: boolean
-  template: string
-  loading: boolean
-}
-const GCsheet: React.FC<iGCsheetPorps> = ({ modalHandler }) => {
-  const [ sectionTemplate, setSectionTemplate ] = useState<iShowTemplateState>({
-    isShow: false,
-    template: '',
-    loading: false
-  })
-  const [ projectTree, setProjectTree ] = useState<{}>({})
-  const [ tempalte, setTempalte ] = useState<{template1: iTemplateState, template2: iTemplateState}>({
-    template1: {
-      attribution: '',
-      children: undefined,
-      depth: 0,
-      id: 0,
-      isEnd: false,
-      leaf: false,
-      name: '',
-      parentId: 0,
-      serial: ''
-    },
-    template2: {
-      attribution: '',
-      children: undefined,
-      depth: 0,
-      id: 0,
-      isEnd: false,
-      leaf: false,
-      name: '',
-      parentId: 0,
-      serial: ''
-    }
-  })
-  const { TabPane } = Tabs
-  const initSpread = (spread: GC.Spread.Sheets.Workbook) => {
-    spread.suspendPaint()
-    spread.resumePaint()
-  }
-  useEffect(() => {
-    initHandler()
-  }, [])
-  const initHandler = async () => {
-    const data  = await apiContractIncome(tenderState.bidsectionId)
-    if (data.code === consts.RET_CODE.SUCCESS) {
-      if (data.isTemplate && data.isTemplate === 1) {
-        setSectionTemplate({
-          ...sectionTemplate,
-          isShow: true
-        })
-        setTempalte({
-          ...tempalte,
-          template1: data.sectionTemplate1,
-          template2: data.sectionTemplate2
-        })
-      } else {
-        setProjectTree(data.data)
-      }
-    }
-  }
-  const columns = [
-    {
-      title: '项目节',
-      dataIndex: 'serial',
-      width: '30%',
-      // eslint-disable-next-line react/display-name
-      render: (text: string, row: iTemplateState) => {
-        const { attribution = '', serial = '' } = row
-        return <span>{`${attribution}${serial}`}</span>
-      }
-    },
-    {
-      title: '名称',
-      dataIndex: 'name',
-      width: '70%'
-    }
-  ]
-  useActivate(() =>{
-    initHandler()
-  })
+// import { tenderStore } from '@/store/mobx'
+// import consts from '@/utils/consts'
+// import GC from '@grapecity/spread-sheets'
+// import { SpreadSheets, Worksheet } from '@grapecity/spread-sheets-react'
+// import { Button, message, Radio, Table, Tabs } from 'antd'
+// import Modal from 'antd/lib/modal/Modal'
+// import { RadioChangeEvent } from 'antd/lib/radio'
+// import React, { useEffect, useState } from 'react'
+// import { useActivate } from 'react-activation'
+// import { apiContractIncome, apiSetTemplate } from './components/Modal/api'
+// import Detail from './components/Tabs/Detail'
+// import File from './components/Tabs/File'
+// import Receivable from './components/Tabs/Receivable'
+// import styles from './index.module.scss'
+// GC.Spread.Common.CultureManager.culture("zh-cn")
+// // GC.Spread.Sheets.LicenseKey = secret
+// interface iGCsheetPorps {
+//   modalHandler: (type: string) => void
+// }
+// interface iTemplateState {
+//   attribution: string
+//   children: iTemplateState[] | undefined
+//   depth: number
+//   id: number
+//   isEnd: boolean
+//   leaf: boolean
+//   name: string
+//   parentId: number
+//   serial: string
+// }
+// interface iShowTemplateState {
+//   isShow: boolean
+//   template: string
+//   loading: boolean
+// }
+// const GCsheet: React.FC<iGCsheetPorps> = ({ modalHandler }) => {
+//   const [ sectionTemplate, setSectionTemplate ] = useState<iShowTemplateState>({
+//     isShow: false,
+//     template: '',
+//     loading: false
+//   })
+//   const [ tempalte, setTempalte ] = useState<{template1: iTemplateState, template2: iTemplateState}>({
+//     template1: {
+//       attribution: '',
+//       children: undefined,
+//       depth: 0,
+//       id: 0,
+//       isEnd: false,
+//       leaf: false,
+//       name: '',
+//       parentId: 0,
+//       serial: ''
+//     },
+//     template2: {
+//       attribution: '',
+//       children: undefined,
+//       depth: 0,
+//       id: 0,
+//       isEnd: false,
+//       leaf: false,
+//       name: '',
+//       parentId: 0,
+//       serial: ''
+//     }
+//   })
+//   const { TabPane } = Tabs
+//   const initSpread = (spread: GC.Spread.Sheets.Workbook) => {
+//     spread.suspendPaint()
+//     spread.resumePaint()
+//   }
+//   useEffect(() => {
+//     initHandler()
+//   }, [])
+//   const initHandler = async () => {
+//     const data  = await apiContractIncome(tenderStore.bidsectionId)
+//     if (data.code === consts.RET_CODE.SUCCESS) {
+//       if (data.isTemplate && data.isTemplate === 1) {
+//         setSectionTemplate({
+//           ...sectionTemplate,
+//           isShow: true
+//         })
+//         setTempalte({
+//           ...tempalte,
+//           template1: data.sectionTemplate1,
+//           template2: data.sectionTemplate2
+//         })
+//       } else {
+//         setProjectTree(data.data)
+//       }
+//     }
+//   }
+//   const columns = [
+//     {
+//       title: '项目节',
+//       dataIndex: 'serial',
+//       width: '30%',
+//       // eslint-disable-next-line react/display-name
+//       render: (text: string, row: iTemplateState) => {
+//         const { attribution = '', serial = '' } = row
+//         return <span>{`${attribution}${serial}`}</span>
+//       }
+//     },
+//     {
+//       title: '名称',
+//       dataIndex: 'name',
+//       width: '70%'
+//     }
+//   ]
+//   useActivate(() =>{
+//     initHandler()
+//   })
 
-  // modal 确认 - 回调
-  const handleModalConfirm = async () => {
-    setSectionTemplate({
-      ...sectionTemplate,
-      loading: true
-    })
-    if (!sectionTemplate.template) return message.error('请选择项目节模板!')
-    const { code = -1 } = await apiSetTemplate(sectionTemplate.template, tenderState.bidsectionId)
-    if (code === consts.RET_CODE.SUCCESS) {
-      await initHandler()
-    }
-    setSectionTemplate({
-      ...sectionTemplate,
-      loading: false,
-      isShow: false
-    })
-  }
-  // radio 切换回调
-  const handleRadioEvent = (e: RadioChangeEvent) => {
-    if (e.target.checked) {
-      setSectionTemplate({
-        ...sectionTemplate,
-        template: e.target.value
-      })
-    }
+//   // modal 确认 - 回调
+//   const handleModalConfirm = async () => {
+//     setSectionTemplate({
+//       ...sectionTemplate,
+//       loading: true
+//     })
+//     if (!sectionTemplate.template) return message.error('请选择项目节模板!')
+//     const { code = -1 } = await apiSetTemplate(sectionTemplate.template, tenderStore.bidsectionId)
+//     if (code === consts.RET_CODE.SUCCESS) {
+//       await initHandler()
+//     }
+//     setSectionTemplate({
+//       ...sectionTemplate,
+//       loading: false,
+//       isShow: false
+//     })
+//   }
+//   // radio 切换回调
+//   const handleRadioEvent = (e: RadioChangeEvent) => {
+//     if (e.target.checked) {
+//       setSectionTemplate({
+//         ...sectionTemplate,
+//         template: e.target.value
+//       })
+//     }
 
-  }
-  return sectionTemplate.isShow ?
-  <Modal
-    visible={sectionTemplate.isShow}
-    maskClosable={false}
-    title="选择合同项目节模板"
-    okText="确定"
-    confirmLoading={sectionTemplate.loading}
-    cancelText="关闭"
-    closable={false}
-    keyboard={false}
-    onOk={() => handleModalConfirm()}
-    width='70vw'
-  >
-    <div className={styles.modalWarnText}>默认项目节无法修改,可自行增加维护子节点</div>
-    <div className={styles.modalTemplateContent}>
-      <div className={styles.leftTemplate}>
-        <div className="pi-pd-20">
-          <Radio value="1" checked={sectionTemplate.template === '1'} onChange={(e: RadioChangeEvent) => handleRadioEvent(e)}><span className="pi-gray-color">项目节模板1</span></Radio>
-        </div>
-        <div className={styles.projectTable}>
-          {
-            tempalte.template1?.children &&  tempalte.template1?.children.length?
-            <Table
-            dataSource={tempalte.template1?.children}
-            columns={columns}
-            pagination={false}
-            bordered
-            scroll={{ y: '300px' }}
-            rowKey={record => record.id}
-            defaultExpandAllRows={true}
-            >
-          </Table> : ''
-          }
-        </div>
+//   }
+//   return sectionTemplate.isShow ?
+//   <Modal
+//     visible={sectionTemplate.isShow}
+//     maskClosable={false}
+//     title="选择合同项目节模板"
+//     okText="确定"
+//     confirmLoading={sectionTemplate.loading}
+//     cancelText="关闭"
+//     closable={false}
+//     keyboard={false}
+//     onOk={() => handleModalConfirm()}
+//     width='70vw'
+//   >
+//     <div className={styles.modalWarnText}>默认项目节无法修改,可自行增加维护子节点</div>
+//     <div className={styles.modalTemplateContent}>
+//       <div className={styles.leftTemplate}>
+//         <div className="pi-pd-20">
+//           <Radio value="1" checked={sectionTemplate.template === '1'} onChange={(e: RadioChangeEvent) => handleRadioEvent(e)}><span className="pi-gray-color">项目节模板1</span></Radio>
+//         </div>
+//         <div className={styles.projectTable}>
+//           {
+//             tempalte.template1?.children &&  tempalte.template1?.children.length?
+//             <Table
+//             dataSource={tempalte.template1?.children}
+//             columns={columns}
+//             pagination={false}
+//             bordered
+//             scroll={{ y: '300px' }}
+//             rowKey={record => record.id}
+//             defaultExpandAllRows={true}
+//             >
+//           </Table> : ''
+//           }
+//         </div>
 
-      </div>
-      <div className={styles.rightTemplate}>
-        <div className="pi-pd-20 pi-gray-color">
-          <Radio value="2" checked={sectionTemplate.template === '2'} onChange={(e: RadioChangeEvent) => handleRadioEvent(e)}><span className="pi-gray-color">项目节模板2</span></Radio>
-        </div>
-        <div className={styles.projectTable}>
-          {
-            tempalte.template2?.children &&  tempalte.template2?.children.length? <Table
-            dataSource={tempalte.template2?.children}
-            columns={columns}
-            bordered
-            pagination={false}
-            scroll={{ y: '300px' }}
-            rowKey={record => record.id}
-            defaultExpandAllRows={true}
-            >
-          </Table> : ''
-          }
+//       </div>
+//       <div className={styles.rightTemplate}>
+//         <div className="pi-pd-20 pi-gray-color">
+//           <Radio value="2" checked={sectionTemplate.template === '2'} onChange={(e: RadioChangeEvent) => handleRadioEvent(e)}><span className="pi-gray-color">项目节模板2</span></Radio>
+//         </div>
+//         <div className={styles.projectTable}>
+//           {
+//             tempalte.template2?.children &&  tempalte.template2?.children.length? <Table
+//             dataSource={tempalte.template2?.children}
+//             columns={columns}
+//             bordered
+//             pagination={false}
+//             scroll={{ y: '300px' }}
+//             rowKey={record => record.id}
+//             defaultExpandAllRows={true}
+//             >
+//           </Table> : ''
+//           }
 
-        </div>
-      </div>
-    </div>
-  </Modal>
-  :
-  <div className={styles.spreadContent}>
-    <div className={styles.spreadSheets}>
-      <SpreadSheets
-        workbookInitialized={(spread: GC.Spread.Sheets.Workbook)=> initSpread(spread)}
-        showHorizontalScrollbar={false}
-        showVerticalScrollbar={false}
-        tabStripVisible={false}
-        >
-        <Worksheet rowHeaderVisible={false}>
-        </Worksheet>
-    </SpreadSheets>
-    </div>
-    <div className={styles.extraControl}>
-      <Tabs
-        type="card"
-        size="small"
-        defaultActiveKey="1"
-        tabBarExtraContent={{ right: <div className="pi-mg-right-5"><Button type="primary" size="small" danger className="pi-mg-right-3" onClick={() => modalHandler('close')}>关闭合同</Button><Button type="primary" size="small" onClick={() => modalHandler('edit')}>编辑合同</Button></div> }}>
-        <TabPane key="1" tab="合同详情">
-          <Detail></Detail>
-        </TabPane>
-        <TabPane key="2" tab="合同回款">
-          <Receivable></Receivable>
-        </TabPane>
-        <TabPane key="3" tab="合同文件">
-          <File></File>
-        </TabPane>
-      </Tabs>
-    </div>
-  </div>
-}
+//         </div>
+//       </div>
+//     </div>
+//   </Modal>
+//   :
+//   <div className={styles.spreadContent}>
+//     <div className={styles.spreadSheets}>
+//       <SpreadSheets
+//         workbookInitialized={(spread: GC.Spread.Sheets.Workbook)=> initSpread(spread)}
+//         showHorizontalScrollbar={false}
+//         showVerticalScrollbar={false}
+//         tabStripVisible={false}
+//         >
+//         <Worksheet rowHeaderVisible={false}>
+//         </Worksheet>
+//     </SpreadSheets>
+//     </div>
+//     <div className={styles.extraControl}>
+//       <Tabs
+//         type="card"
+//         size="small"
+//         defaultActiveKey="1"
+//         tabBarExtraContent={{ right: <div className="pi-mg-right-5"><Button type="primary" size="small" danger className="pi-mg-right-3" onClick={() => modalHandler('close')}>关闭合同</Button><Button type="primary" size="small" onClick={() => modalHandler('edit')}>编辑合同</Button></div> }}>
+//         <TabPane key="1" tab="合同详情">
+//           <Detail></Detail>
+//         </TabPane>
+//         <TabPane key="2" tab="合同回款">
+//           <Receivable></Receivable>
+//         </TabPane>
+//         <TabPane key="3" tab="合同文件">
+//           <File></File>
+//         </TabPane>
+//       </Tabs>
+//     </div>
+//   </div>
+// }
 
-export default GCsheet
+// // function formatSheetData(tree: iTemplateState[]): {
+
+// // }
+// export default GCsheet

+ 17 - 6
src/pages/Contract/Content/Income/index.module.scss

@@ -1,3 +1,9 @@
+.projectTable {
+  :global(.ant-table-cell) {
+    padding: 0.3rem;
+  }
+}
+
 .spreadContent {
   position: relative;
   height: calc(100vh - 34px);
@@ -5,6 +11,17 @@
   .spreadSheets {
     width: 100%;
     height: 62%;
+    overflow: hidden;
+    :global(.ant-table-tbody > tr > td) {
+      padding: 0.3rem;
+    }
+    :global(.ant-table-thead > tr > th) {
+      padding: 0.3rem;
+      color: #000000;
+      background: #e9ecef;
+      border-bottom: 2px solid #dee2e6;
+      border-bottom-width: 2px;
+    }
   }
   .extraControl {
     width: 100%;
@@ -40,9 +57,3 @@
     width: 45%;
   }
 }
-
-.projectTable {
-  :global(.ant-table-cell) {
-    padding: 0.3rem;
-  }
-}

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

@@ -0,0 +1,289 @@
+import { contractStore, tenderStore } from '@/store/mobx'
+import { iIncomeTree } from '@/types/contract'
+import { contractConsts } from '@/utils/common/constStatus'
+import consts from '@/utils/consts'
+import { Button, message, Radio, Table, Tabs } from 'antd'
+import Modal from 'antd/lib/modal/Modal'
+import { RadioChangeEvent } from 'antd/lib/radio'
+import { ColumnsType } from 'antd/lib/table'
+import { observer } from 'mobx-react'
+import React, { useEffect, useState } from 'react'
+import { useActivate } from 'react-activation'
+import { apiContractIncome, apiSetTemplate } from '../Modal/api'
+import Detail from '../Tabs/Detail'
+import File from '../Tabs/File'
+import Receivable from '../Tabs/Receivable'
+import styles from './index.module.scss'
+interface iTableContentPorps {
+  modalHandler: (type: string) => void
+  row: iIncomeTree
+  setRow: (record: iIncomeTree) => void
+}
+interface iTemplateState {
+  attribution: string
+  children: iTemplateState[] | undefined
+  depth: number
+  id: number
+  isEnd: boolean
+  leaf: boolean
+  name: string
+  parentId: number
+  serial: string
+}
+interface iShowTemplateState {
+  isShow: boolean
+  template: string
+  loading: boolean
+}
+
+const GCsheet: React.FC<iTableContentPorps> = ({ modalHandler, row, setRow }) => {
+  const [ sectionTemplate, setSectionTemplate ] = useState<iShowTemplateState>({
+    isShow: false,
+    template: '',
+    loading: false
+  })
+  const [ tempalte, setTempalte ] = useState<{template1: iTemplateState, template2: iTemplateState}>({
+    template1: {
+      attribution: '',
+      children: undefined,
+      depth: 0,
+      id: 0,
+      isEnd: false,
+      leaf: false,
+      name: '',
+      parentId: 0,
+      serial: ''
+    },
+    template2: {
+      attribution: '',
+      children: undefined,
+      depth: 0,
+      id: 0,
+      isEnd: false,
+      leaf: false,
+      name: '',
+      parentId: 0,
+      serial: ''
+    }
+  })
+  const { TabPane } = Tabs
+
+  useEffect(() => {
+    initHandler()
+  }, [])
+
+  const initHandler = async () => {
+    const data  = await apiContractIncome(tenderStore.bidsectionId)
+    if (data.code === consts.RET_CODE.SUCCESS) {
+      if (data.isTemplate && data.isTemplate === 1) {
+        setSectionTemplate({
+          ...sectionTemplate,
+          isShow: true
+        })
+        setTempalte({
+          ...tempalte,
+          template1: data.sectionTemplate1,
+          template2: data.sectionTemplate2
+        })
+      } else {
+        contractStore.updateTree(data.sectionTree)
+      }
+    }
+  }
+  const modalColumns: ColumnsType<iTemplateState> = [
+    {
+      title: '项目节',
+      dataIndex: 'serial',
+      width: '30%',
+      // eslint-disable-next-line react/display-name
+      render: (text: string, row: iTemplateState) => {
+        const { attribution = '', serial = '' } = row
+        return <span>{`${attribution}${serial}`}</span>
+      }
+    },
+    {
+      title: '名称',
+      dataIndex: 'name',
+      width: '70%'
+    }
+  ]
+
+  const TableColumns: ColumnsType<iIncomeTree> = [
+    {
+      title: '编号',
+      dataIndex: 'code',
+      width: '10%'
+    },
+    {
+      title: '项目名称',
+      dataIndex: 'name'
+    },
+    {
+      title: '合同名称',
+      dataIndex: 'contractName'
+    },
+    {
+      title: '合同编号',
+      dataIndex: 'contractCode'
+    },
+    {
+      title: '合同金额',
+      dataIndex: 'contractPrice',
+      align: 'right'
+    },
+    {
+      title: '回款金额',
+      dataIndex: 'contractReturned',
+      align: 'right'
+    },
+    {
+      title: '状态',
+      dataIndex: 'contractStatus',
+      // eslint-disable-next-line react/display-name
+    render: (_: any, record: iIncomeTree) =>
+      <span><i className={contractConsts[record.contractStatus].className}></i>{contractConsts[record.contractStatus].text}</span>
+    }
+
+  ]
+
+  useActivate(() =>{
+    initHandler()
+  })
+
+  // modal 确认 - 回调
+  const handleModalConfirm = async () => {
+    setSectionTemplate({
+      ...sectionTemplate,
+      loading: true
+    })
+    if (!sectionTemplate.template) return message.error('请选择项目节模板!')
+    const { code = -1 } = await apiSetTemplate(sectionTemplate.template, tenderStore.bidsectionId)
+    if (code === consts.RET_CODE.SUCCESS) {
+      await initHandler()
+    }
+    setSectionTemplate({
+      ...sectionTemplate,
+      loading: false,
+      isShow: false
+    })
+  }
+  // 模板选择radio切换回调
+  const handleRadioEvent = (e: RadioChangeEvent) => {
+    if (e.target.checked) {
+      setSectionTemplate({
+        ...sectionTemplate,
+        template: e.target.value
+      })
+    }
+  }
+
+  const onClickRow  = (record: iIncomeTree) => {
+    return {
+      onClick() {
+        console.log(record)
+
+        setRow(record)
+      }
+    }
+  }
+  const handleRowClass = (record: iIncomeTree) => {
+    return record.id === row.id ? 'ant-table-row-selected' : ''
+  }
+  return sectionTemplate.isShow ?
+  <Modal
+    visible={sectionTemplate.isShow}
+    maskClosable={false}
+    title="选择合同项目节模板"
+    okText="确定"
+    confirmLoading={sectionTemplate.loading}
+    cancelText="关闭"
+    closable={false}
+    keyboard={false}
+    onOk={() => handleModalConfirm()}
+    width='70vw'
+  >
+    <div className={styles.modalWarnText}>默认项目节无法修改,可自行增加维护子节点</div>
+    <div className={styles.modalTemplateContent}>
+      <div className={styles.leftTemplate}>
+        <div className="pi-pd-20">
+          <Radio value="1" checked={sectionTemplate.template === '1'} onChange={(e: RadioChangeEvent) => handleRadioEvent(e)}><span className="pi-gray-color">项目节模板1</span></Radio>
+        </div>
+        <div className={styles.projectTable}>
+          {
+            tempalte.template1?.children &&  tempalte.template1?.children.length?
+            <Table
+            dataSource={tempalte.template1?.children}
+            columns={modalColumns}
+            pagination={false}
+            bordered
+            scroll={{ y: '300px' }}
+            rowKey={record => record.id}
+            defaultExpandAllRows={true}
+            >
+          </Table> : ''
+          }
+        </div>
+
+      </div>
+      <div className={styles.rightTemplate}>
+        <div className="pi-pd-20 pi-gray-color">
+          <Radio value="2" checked={sectionTemplate.template === '2'} onChange={(e: RadioChangeEvent) => handleRadioEvent(e)}><span className="pi-gray-color">项目节模板2</span></Radio>
+        </div>
+        <div className={styles.projectTable}>
+          {
+            tempalte.template2?.children &&  tempalte.template2?.children.length? <Table
+            dataSource={tempalte.template2?.children}
+            columns={modalColumns}
+            bordered
+            pagination={false}
+            scroll={{ y: '300px' }}
+            rowKey={record => record.id}
+            defaultExpandAllRows={true}
+            >
+          </Table> : ''
+          }
+
+        </div>
+      </div>
+    </div>
+  </Modal>
+  :
+  <div className={styles.spreadContent}>
+    <div className={styles.spreadSheets}>
+      {
+        contractStore.showTable ?
+        <Table<iIncomeTree>
+          dataSource={contractStore.tree.children}
+          columns={TableColumns}
+          bordered
+          pagination={false}
+          rowKey={record => record.id}
+          defaultExpandAllRows={true}
+          onRow={onClickRow}
+          rowClassName={handleRowClass}
+          style={{ height: '100%', overflowY: 'scroll' }}
+        />
+        : ''
+      }
+    </div>
+    <div className={styles.extraControl}>
+      <Tabs
+        type="card"
+        size="small"
+        defaultActiveKey="1"
+        tabBarExtraContent={{ right: <div className="pi-mg-right-5"><Button type="primary" size="small" danger className="pi-mg-right-3" onClick={() => modalHandler('close')}>关闭合同</Button><Button type="primary" size="small" onClick={() => modalHandler('edit')}>编辑合同</Button></div> }}>
+        <TabPane key="1" tab="合同详情">
+          <Detail></Detail>
+        </TabPane>
+        <TabPane key="2" tab="合同回款">
+          <Receivable></Receivable>
+        </TabPane>
+        <TabPane key="3" tab="合同文件">
+          <File></File>
+        </TabPane>
+      </Tabs>
+    </div>
+  </div>
+}
+
+export default observer(GCsheet)

+ 57 - 24
src/pages/Contract/Content/Income/index.tsx

@@ -1,14 +1,14 @@
 import Header from '@/components/Header'
 import Slot from '@/components/Header/slot'
-import { iModalBooleanProps } from '@/types/contract'
+import { iIncomeTree, iModalBooleanProps } from '@/types/contract'
+import { contractTreeBaseId } from '@/utils/common/constStatus'
 import consts from '@/utils/consts'
 import { ArrowDownOutlined, ArrowLeftOutlined, ArrowRightOutlined, ArrowUpOutlined, CloseOutlined, PlusOutlined, SettingOutlined } from '@ant-design/icons'
-import '@grapecity/spread-sheets/styles/gc.spread.sheets.excel2016colorful.css'
 import { Button, Tooltip } from 'antd'
 import React, { useState } from 'react'
 import { apiContractSheet } from './api'
 import ContractModal from './components/Modal'
-import GCsheet from './GCsheet'
+import TableContent from './components/TableContent'
 
 export default function Income() {
   const [ modalObj, setModalObj ] = useState<iModalBooleanProps>({
@@ -16,6 +16,29 @@ export default function Income() {
     visible: false,
     confirmLoading: false
   })
+  const [ row, setRow ] = useState<iIncomeTree>({
+    attribsortution: 0,
+    attribution: '',
+    bidsectionId: '',
+    children: undefined,
+    code: '',
+    contractCode: '',
+    contractId: '',
+    contractName: '',
+    contractPrice: '',
+    contractReturned: '',
+    contractStatus: 0,
+    contractsPaid: '',
+    createTime: '',
+    depth: 0,
+    id: '',
+    name: '',
+    operation: '',
+    parentId: '',
+    projectId: '',
+    serial: 0,
+    templateNumber: 0
+  })
 
   const onCreate = async (value: any, type: any) => {
     setModalObj({
@@ -45,29 +68,40 @@ export default function Income() {
       <Header title="维护项目节:">
         <Slot position="left">
           <div className="pi-flex-row">
-              <Tooltip title="添加子项">
-                <Button type="text" icon={<PlusOutlined style={{ color: '#007bff' }}/>}></Button>
-              </Tooltip>
-
-              <Tooltip title="删除">
-                <Button type="text" icon={<CloseOutlined style={{ color: '#007bff' }}/>}/>
-              </Tooltip>
+            {
+              row.id ?
+              <>
+                <Tooltip title="添加子项">
+                  <Button type="text" icon={<PlusOutlined style={{ color: '#007bff' }}/>}></Button>
+                </Tooltip>
+                {
+                  !row.children ?
+                  <Tooltip title="删除">
+                    <Button type="text" icon={<CloseOutlined style={{ color: '#007bff' }}/>}/>
+                  </Tooltip> : ''
+                }
+                {
+                  row.parentId && row.parentId !== contractTreeBaseId ?
+                  <Tooltip title="升级">
+                    <Button type="text" icon={<ArrowLeftOutlined />} style={{ color: '#007bff' }}></Button>
+                  </Tooltip> : ''
+                }
 
-              <Tooltip title="升级">
-                <Button type="text" icon={<ArrowLeftOutlined />} style={{ color: '#007bff' }}></Button>
-              </Tooltip>
+                <Tooltip title="降级">
+                  <Button type="text" icon={<ArrowRightOutlined style={{ color: '#007bff' }}/>}></Button>
+                </Tooltip>
 
-              <Tooltip title="降级">
-                <Button type="text" icon={<ArrowRightOutlined style={{ color: '#007bff' }}/>}></Button>
-              </Tooltip>
+                <Tooltip title="下移">
+                  <Button type="text" icon={<ArrowUpOutlined style={{ color: '#007bff' }}/>}></Button>
+                </Tooltip>
 
-              <Tooltip title="下移">
-                <Button type="text" icon={<ArrowUpOutlined style={{ color: '#007bff' }}/>}></Button>
-              </Tooltip>
+                <Tooltip title="上移">
+                  <Button type="text" icon={<ArrowDownOutlined style={{ color: '#007bff' }}/>}></Button>
+                </Tooltip>
+              </>
+              : ''
+            }
 
-              <Tooltip title="上移">
-                <Button type="text" icon={<ArrowDownOutlined style={{ color: '#007bff' }}/>}></Button>
-              </Tooltip>
           </div>
         </Slot>
         <Slot position="right">
@@ -75,9 +109,8 @@ export default function Income() {
           <Button type="primary" size="small" onClick={() => setModalObj({ ...modalObj, type: 'add', visible: true })}>新建收入合同</Button>
         </Slot>
       </Header>
-      <GCsheet modalHandler={modalHandler}></GCsheet>
+      <TableContent modalHandler={modalHandler} row={row} setRow={(record: iIncomeTree) => setRow(record)}></TableContent>
       <ContractModal modalObj={modalObj} onConfirm={onCreate} onCancel={ () => setModalObj({ ...modalObj, visible: false })}></ContractModal>
     </div>
   )
 }
-

+ 8 - 2
src/pages/Contract/Content/Spending/index.tsx

@@ -1,9 +1,15 @@
+import { contractStore } from '@/store/mobx'
+import { observer } from 'mobx-react'
 import React from 'react'
 
-export default function Spending() {
+const Spending:React.FC<{}> = () => {
+
   return (
     <div>
-<h5>111</h5>
+      <h5>{contractStore.id}</h5>
+      <button onClick={() => contractStore.updateId()}>change</button>
     </div>
   )
 }
+
+export default observer(Spending)

+ 3 - 5
src/pages/Login/index.tsx

@@ -1,9 +1,8 @@
 import logo from '@/assets/img/loginlogo.png'
-import { userState } from '@/store/mobx'
+import { userStore } from '@/store/mobx'
 import { iFromValues, iLoginProps, iRetrieveFormProps } from '@/types/login'
 import consts from '@/utils/consts'
 import { Button, Form, Input, Modal } from 'antd'
-import { observer } from "mobx-react"
 import React, { Component } from 'react'
 import { RouteComponentProps, withRouter } from 'react-router-dom'
 import { apiProject } from "./api"
@@ -14,11 +13,10 @@ const initLoginState = {
   projectInfo: '',
   projectCode: '',
   visible: false,
-  user: userState.userInfo
+  user: userStore.userInfo
 }
 
 type iState = typeof initLoginState
-@observer
 class NormalLoginForm extends Component<iLoginProps, iState> {
 
   constructor(props: iLoginProps) {
@@ -29,7 +27,7 @@ class NormalLoginForm extends Component<iLoginProps, iState> {
     // const { code = -1, data }  = await apiLogin(values)
     // if (code === consts.RET_CODE.SUCCESS) {
       // }
-    userState.login(values)
+    userStore.login(values)
   }
 
   componentWillUnmount() {

+ 47 - 0
src/store/mobx/contract/index.ts

@@ -0,0 +1,47 @@
+import { iIncomeTree } from "@/types/contract"
+import { action, computed, observable } from "mobx"
+
+class Contract {
+  @observable tree: iIncomeTree = {
+    attribsortution: 0,
+    attribution: '',
+    bidsectionId: '',
+    children: undefined,
+    code: '',
+    contractCode: '',
+    contractId: '',
+    contractName: '',
+    contractPrice: '',
+    contractReturned: '',
+    contractStatus: 0,
+    contractsPaid: '',
+    createTime: '',
+    depth: 0,
+    id: '',
+    name: '',
+    operation: '',
+    parentId: '',
+    projectId: '',
+    serial: 0,
+    templateNumber: 0
+  }
+
+  @observable id: string = Math.random() + ''
+
+  @action updateId() {
+    console.log('执行了')
+
+    this.id = Math.random() + ''
+  }
+
+  @action updateTree(tree: iIncomeTree) {
+    console.log(tree)
+    this.tree = tree
+  }
+
+  @computed showTable() {
+    return this.tree.children && this.tree.children.length
+  }
+}
+
+export default new Contract()

+ 8 - 6
src/store/mobx/index.ts

@@ -1,9 +1,11 @@
-import frameState from './frame'
-import tenderState from './tender'
-import userState from './user'
+import contractStore from './contract'
+import frameStore from './frame'
+import tenderStore from './tender'
+import userStore from './user'
 export {
-  userState,
-  frameState,
-  tenderState
+  userStore,
+  frameStore,
+  tenderStore,
+  contractStore
 }
 

+ 24 - 0
src/types/contract.d.ts

@@ -19,6 +19,30 @@ export interface ContractTree {
   parentId: string;
   projectId: string;
 }
+
+export interface iIncomeTree {
+  attribsortution: number;
+  attribution: string;
+  bidsectionId: string;
+  children: iIncomeTree[] | undefined;
+  code: string;
+  contractCode: string;
+  contractId: string;
+  contractName: string;
+  contractPrice: string;
+  contractReturned: string;
+  contractStatus: number;
+  contractsPaid: string;
+  createTime: string;
+  depth: number;
+  id: string;
+  name: string;
+  operation: string;
+  parentId: string;
+  projectId: string;
+  serial: number;
+  templateNumber: number;
+}
 export interface iModalBooleanProps {
   type : string
   visible: boolean

+ 20 - 0
src/utils/common/constStatus.ts

@@ -0,0 +1,20 @@
+const contractConsts: object = {
+  0: {
+    className: 'pi-circle-yellow',
+    text: '履行中'
+  },
+  1: {
+    className: 'pi-circle-gray',
+    text: '待关闭'
+  },
+  2: {
+    className: 'pi-circle-green',
+    text: '正常关闭'
+  }
+}
+
+const contractTreeBaseId: string = 'xCi4xUL6uur0h7fVI--NeA'
+export {
+  contractConsts,
+  contractTreeBaseId
+}

+ 1 - 0
src/utils/util.ts

@@ -87,6 +87,7 @@ const dayjsFomrat = (date: dayjs.ConfigType, format: string = 'YYYY-MM-DD HH:mm:
   return dayjs(date).format(format)
 }
 
+
 export {
   getCookie,
   storage,