Selaa lähdekoodia

feat: 合同管理弹窗组件

lanjianrong 4 vuotta sitten
vanhempi
commit
890a5a5ff9

+ 82 - 0
src/assets/css/common.scss

@@ -1,3 +1,37 @@
+.pi-success {
+  background-color: #28a745;
+}
+
+.pi-gray {
+  background-color: #bbbbbb;
+}
+
+.pi-blue {
+  background-color: #007bff;
+}
+
+.pi-red {
+  background-color: #dc3545;
+}
+
+.pi-link-blue {
+  color: #007bff;
+  &:hover {
+    text-decoration: underline;
+    color: #0056b3;
+    cursor: pointer;
+  }
+}
+
+.pi-link-red {
+  color: #dc3545;
+  &:hover {
+    text-decoration: underline;
+    color: #af1d2a;
+    cursor: pointer;
+  }
+}
+
 .pi-flex {
   display: flex;
 }
@@ -318,3 +352,51 @@
 .pi-gray-color {
   color: #757575;
 }
+
+.pi-circle-gray {
+  position: relative;
+  padding-right: 14px;
+  &::before {
+    content: '';
+    position: absolute;
+    top: 50%;
+    transform: translateY(-50%);
+    left: 0;
+    width: 12px;
+    height: 12px;
+    background-color: #6c757d;
+    border-radius: 50%;
+  }
+}
+.pi-circle-yellow {
+  position: relative;
+  padding-right: 14px;
+  &::before {
+    content: '';
+    position: absolute;
+    top: 0;
+    top: 50%;
+    transform: translateY(-50%);
+    left: 0;
+    width: 12px;
+    height: 12px;
+    background-color: #ffc170;
+    border-radius: 50%;
+  }
+}
+.pi-circle-green {
+  position: relative;
+  padding-right: 14px;
+  &::before {
+    content: '';
+    position: absolute;
+    top: 0;
+    left: 0;
+    top: 50%;
+    transform: translateY(-50%);
+    width: 12px;
+    height: 12px;
+    background-color: #28a745;
+    border-radius: 50%;
+  }
+}

+ 25 - 7
src/pages/Contract/Content/Income/components/GCsheet.tsx

@@ -1,14 +1,20 @@
 import { tenderState } from '@/store/mobx'
 import consts from '@/utils/consts'
+import secret from '@/utils/sheets'
 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 { apiContractIncome, apiSetTemplate } from './api'
-import styles from './GCsheet.module.scss'
+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
 }
@@ -89,7 +95,7 @@ const GCsheet: React.FC<iGCsheetPorps> = ({ modalHandler }) => {
     {
       title: '项目节',
       dataIndex: 'serial',
-      width: '40%',
+      width: '30%',
       // eslint-disable-next-line react/display-name
       render: (text: string, row: iTemplateState) => {
         const { attribution = '', serial = '' } = row
@@ -99,9 +105,13 @@ const GCsheet: React.FC<iGCsheetPorps> = ({ modalHandler }) => {
     {
       title: '名称',
       dataIndex: 'name',
-      width: '60%'
+      width: '70%'
     }
   ]
+  useActivate(() =>{
+    initHandler()
+  })
+
   // modal 确认 - 回调
   const handleModalConfirm = async () => {
     setSectionTemplate({
@@ -155,6 +165,7 @@ const GCsheet: React.FC<iGCsheetPorps> = ({ modalHandler }) => {
             dataSource={tempalte.template1?.children}
             columns={columns}
             pagination={false}
+            bordered
             scroll={{ y: '300px' }}
             rowKey={record => record.id}
             defaultExpandAllRows={true}
@@ -173,6 +184,7 @@ const GCsheet: React.FC<iGCsheetPorps> = ({ modalHandler }) => {
             tempalte.template2?.children &&  tempalte.template2?.children.length? <Table
             dataSource={tempalte.template2?.children}
             columns={columns}
+            bordered
             pagination={false}
             scroll={{ y: '300px' }}
             rowKey={record => record.id}
@@ -204,9 +216,15 @@ const GCsheet: React.FC<iGCsheetPorps> = ({ modalHandler }) => {
         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="合同详情"></TabPane>
-        <TabPane key="2" tab="合同回款"></TabPane>
-        <TabPane key="3" tab="合同文件"></TabPane>
+        <TabPane key="1" tab="合同详情">
+          <Detail></Detail>
+        </TabPane>
+        <TabPane key="2" tab="合同回款">
+          <Receivable></Receivable>
+        </TabPane>
+        <TabPane key="3" tab="合同文件">
+          <File></File>
+        </TabPane>
       </Tabs>
     </div>
   </div>

src/pages/Contract/Content/Income/components/api.ts → src/pages/Contract/Content/Income/components/Modal/api.ts


+ 31 - 6
src/pages/Contract/Content/Income/components/ContractModal.tsx

@@ -1,26 +1,32 @@
 import { iModalCommonProps } from '@/types/contract'
-import { Button, DatePicker, Form, Input, message, Modal, Select, TreeSelect } from 'antd'
+import { Button, DatePicker, Form, Input, InputNumber, message, Modal, Select, Tabs, TreeSelect } from 'antd'
 import locale from 'antd/es/date-picker/locale/zh_CN'
 import 'moment/locale/zh-cn'
 import React from 'react'
 const ContractModal: React.FC<iModalCommonProps> = ({ modalObj: { type, visible, confirmLoading }, onConfirm, onCancel }) => {
   const { Option } = Select
   const [ form ] = Form.useForm()
+  const { TabPane } = Tabs
   const modalObj = {
     add: {
       title: '新建合同',
-      cancalText: '取消',
+      cancelText: '取消',
       okText: '确认添加'
     },
     edit: {
       title: '编辑合同',
-      cancalText: '取消',
+      cancelText: '取消',
       okText: '确认'
     },
     close: {
       title: '关闭合同',
-      cancalText: '取消',
+      cancelText: '取消',
       okText: '确认关闭'
+    },
+    setting: {
+      title: '合同设置',
+      cancelText: '关闭',
+      okText: '确认'
     }
   }
   return (
@@ -30,7 +36,7 @@ const ContractModal: React.FC<iModalCommonProps> = ({ modalObj: { type, visible,
       onCancel={onCancel}
       footer={[
         type === 'edit' ? <Button type="primary" key="delete" size="small" danger >删除合同</Button> : '',
-        <Button type="primary" size="small" key="cancel" className="pi-btn-secondary" onClick={onCancel}>{modalObj[type]?.cancalText}</Button>,
+        <Button type="primary" size="small" key="cancel" className="pi-btn-secondary" onClick={onCancel}>{modalObj[type]?.cancelText}</Button>,
         <Button type="primary" size="small" key="ok" loading={confirmLoading} onClick={() => {
           form.validateFields().then(values => {
             form.resetFields()
@@ -41,7 +47,7 @@ const ContractModal: React.FC<iModalCommonProps> = ({ modalObj: { type, visible,
         }}>{modalObj[type]?.okText}</Button>
     ]}
     >
-      <Form form={form} layout='vertical' style={{ maxHeight: '482px', overflowY: 'scroll' }}>
+      <Form form={form} layout='vertical' style={type === 'edit' ? { maxHeight: '482px', overflowY: 'scroll' } : { overflow: 'hidden' }}>
         {
           type === 'add' ? (
           <>
@@ -110,6 +116,25 @@ const ContractModal: React.FC<iModalCommonProps> = ({ modalObj: { type, visible,
             </>
           ) : ''
         }
+        {
+          type === 'setting' ? (
+            <>
+              <Tabs defaultActiveKey="1" type="card" size="small">
+                <TabPane tab="合同编号规则" key="1">
+                  <Form.Item name="id1" label="添加新规则组件">
+                    <Select size="small"></Select>
+                  </Form.Item>
+                  <Form.Item name="id2" label="自动编号位数">
+                    <InputNumber size="small"></InputNumber>
+                  </Form.Item>
+                  <Form.Item name="id3" label="起始编号">
+                    <Input size="small"></Input>
+                  </Form.Item>
+                </TabPane>
+              </Tabs>
+            </>
+          ) : ''
+        }
       </Form>
     </Modal>
   )

+ 41 - 0
src/pages/Contract/Content/Income/components/Tabs/Detail/index.module.scss

@@ -0,0 +1,41 @@
+.detailTab {
+  flex: 0 0 66.666667%;
+  max-width: 66.666667%;
+  padding: 0 15px;
+  .detailTable {
+    margin-bottom: 1rem;
+    color: #212529;
+    border: 1px solid #dee2e6;
+    & > tbody tr th {
+      font-weight: normal;
+      color: #000000;
+      background: #e9ecef;
+      border: 1px solid #dee2e6;
+    }
+    & > tbody tr th,
+    & > tbody tr td {
+      padding: 0.3rem;
+      border: 1px solid #dee2e6;
+    }
+    .progressContainer {
+      padding: 0.3rem;
+      .progressContent {
+        display: flex;
+        height: 1rem;
+        overflow: hidden;
+        font-size: 0.75rem;
+        background-color: #e9ecef;
+        border-radius: 0.25rem;
+        .progressBar {
+          display: flex;
+          flex-direction: column;
+          justify-content: center;
+          color: #ffffff;
+          text-align: center;
+          white-space: nowrap;
+          transition: width 0.6s ease;
+        }
+      }
+    }
+  }
+}

+ 24 - 0
src/pages/Contract/Content/Income/components/Tabs/Detail/index.tsx

@@ -0,0 +1,24 @@
+import React from 'react'
+import styles from './index.module.scss'
+export default function Detail() {
+  return (
+    <div className={styles.detailTab}>
+      <table className={styles.detailTable}>
+        <tbody>
+          <tr><th style={{ width: '10%' }}>项目内容</th><td style={{ width: '40%' }}>1.2.2 碎石</td><th style={{ width: '10%' }}>合同名称</th><td style={{ width: '40%' }}>台州湾大桥及接线工程TS15标碎石供应合同</td></tr>
+          <tr><th>合同编号</th><td>CD03-CL-2015-118</td><th>状态</th><td><i className="pi-circle-yellow"></i> 履行中</td></tr>
+          <tr><th>合同金额</th><td>6,557,000.00 </td><th>创建时间</th><td>2020-08-02 14:23 </td></tr>
+          <tr><th>回款金额</th><td>3,787,300.07</td><th>未回款金额</th><td>2,769,700.00</td></tr>
+          <tr><th>回款进度</th><td className={styles.progressContainer} colSpan={3}><div className={styles.progressContent}>
+            <div className={[ styles.progressBar, 'pi-success' ].join(' ')} style={{ width: '57.7%' }} >57.7%</div>
+            <div className={[ styles.progressBar, 'pi-gray' ].join(' ')} style={{ width: '42.3%' }}>42.3%</div>
+          </div></td></tr>
+          <tr><th>甲方</th><td></td><th>甲方签约人</th><td></td></tr>
+          <tr><th>乙方</th><td></td><th>乙方签约人</th><td></td></tr>
+          <tr><th>签约日期</th><td></td><th></th><td></td></tr>
+          <tr><th>备注</th><td colSpan={3}></td></tr>
+        </tbody>
+      </table>
+    </div>
+  )
+}

+ 0 - 0
src/pages/Contract/Content/Income/components/Tabs/File/index.module.scss


+ 33 - 0
src/pages/Contract/Content/Income/components/Tabs/File/index.tsx

@@ -0,0 +1,33 @@
+import Table, { ColumnsType } from 'antd/lib/table'
+import React from 'react'
+
+interface iFileState {
+  id: string
+  contractName: string
+  uploader: string
+  uploadTime: Date
+}
+const File:React.FC<{}> = () => {
+  const columns: ColumnsType<iFileState> = [
+    {
+      dataIndex: 'sort',
+      // eslint-disable-next-line react/display-name
+      render: (_:any, record: iFileState, idx: number) => <span>{idx}</span>
+    },
+    {
+      dataIndex: 'contractName'
+      // render: (_: any) => <a href="#" download></a>
+    },
+    {
+      dataIndex: 'uploader'
+    },
+    {
+      dataIndex: 'uploadTime'
+    }
+  ]
+  return (
+    <Table columns={columns} bordered rowKey={record => record.id}></Table>
+  )
+}
+
+export default File

+ 6 - 0
src/pages/Contract/Content/Income/components/Tabs/Receivable/api.ts

@@ -0,0 +1,6 @@
+import request from '@/utils/common/request'
+
+export async function apiDel(id: string) {
+  const { data } = await request.del('', { id })
+  return data
+}

+ 0 - 0
src/pages/Contract/Content/Income/components/Tabs/Receivable/index.module.scss


+ 87 - 0
src/pages/Contract/Content/Income/components/Tabs/Receivable/index.tsx

@@ -0,0 +1,87 @@
+import { DisconnectOutlined } from '@ant-design/icons'
+import { Popconfirm, Table } from 'antd'
+import Modal from 'antd/lib/modal/Modal'
+import { ColumnsType } from 'antd/lib/table'
+import React, { useState } from 'react'
+interface iReceivableProps {
+  id: string
+  receDate: Date
+  receAmount: number
+  receWay: string
+  receDesigner: string
+  createTime: Date
+  remark: string
+  attacment: number
+}
+const Receivable:React.FC<{}> = () => {
+  const [ visible, setVisible ] = useState(false)
+  const delConfirm = (key: string) => {
+    console.log(key)
+
+  }
+  const data = [
+    {
+      id: '123',
+      receDate: new Date(),
+      receAmount: 199999,
+      receWay: '支票',
+      receDesigner: '张三',
+      createTime: new Date(),
+      remark: '备注内容',
+      attacment: 3
+    }
+  ]
+  const columns: ColumnsType<iReceivableProps> = [
+    {
+      dataIndex: 'sort',
+      // eslint-disable-next-line react/display-name
+      render: (text, record, index) => {
+        return <span>{index + 1}</span>
+      }
+    },
+    {
+      title: '回款日期',
+      dataIndex: 'receDate'
+    },
+    {
+      title: '回款金额',
+      dataIndex: 'receAmount'
+    },
+    {
+      title: '回款方式',
+      dataIndex: 'receWay',
+      align: 'left'
+    },
+    {
+      title: '创建人',
+      dataIndex: 'receDesigner'
+    },
+    {
+      title: '创建时间',
+      dataIndex: 'createTime'
+    },
+    {
+      title: '备注',
+      dataIndex: 'remark'
+    },
+    {
+      title: '附件',
+      dataIndex: 'attacment',
+      // eslint-disable-next-line react/display-name
+      render: (text: number) => <span><DisconnectOutlined />{text}</span>
+    },
+    {
+      title: '操作',
+      dataIndex: 'opreate',
+      // eslint-disable-next-line react/display-name
+      render: (text: any, record: iReceivableProps) => <div><a href="javascript:;" className="pi-link-blue">编辑</a><Popconfirm title="确认删除?" cancelText="取消" okText="确认" onConfirm={() => delConfirm(record.id)}><a href="javascript:;" className="pi-link-red pi-mg-left-5">删除</a></Popconfirm></div>
+    }
+  ]
+  return (
+    <>
+      <Table size="small" dataSource={data} columns={columns} bordered pagination={false} rowKey={record => record.id}></Table>
+      <Modal visible={visible} okText="确认" cancelText="关闭" title="附件"></Modal>
+    </>
+  )
+}
+export default Receivable

src/pages/Contract/Content/Income/components/GCsheet.module.scss → src/pages/Contract/Content/Income/index.module.scss


+ 7 - 10
src/pages/Contract/Content/Income/index.tsx

@@ -1,6 +1,5 @@
 import Header from '@/components/Header'
 import Slot from '@/components/Header/slot'
-import { tenderState } from '@/store/mobx'
 import { iModalBooleanProps } from '@/types/contract'
 import consts from '@/utils/consts'
 import { ArrowDownOutlined, ArrowLeftOutlined, ArrowRightOutlined, ArrowUpOutlined, CloseOutlined, PlusOutlined, SettingOutlined } from '@ant-design/icons'
@@ -8,12 +7,10 @@ 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/ContractModal'
-import GCsheet from './components/GCsheet'
+import ContractModal from './components/Modal'
+import GCsheet from './GCsheet'
 
 export default function Income() {
-  console.log(tenderState.bidsectionId)
-
   const [ modalObj, setModalObj ] = useState<iModalBooleanProps>({
     type: '',
     visible: false,
@@ -56,16 +53,16 @@ export default function Income() {
                 <Button type="text" icon={<CloseOutlined style={{ color: '#007bff' }}/>}/>
               </Tooltip>
 
-              <Tooltip title="左移">
+              <Tooltip title="升级">
                 <Button type="text" icon={<ArrowLeftOutlined />} style={{ color: '#007bff' }}></Button>
               </Tooltip>
 
-              <Tooltip title="右移">
+              <Tooltip title="降级">
                 <Button type="text" icon={<ArrowRightOutlined style={{ color: '#007bff' }}/>}></Button>
               </Tooltip>
 
-            <Tooltip title="下移">
-              <Button type="text" icon={<ArrowUpOutlined style={{ color: '#007bff' }}/>}></Button>
+              <Tooltip title="下移">
+                <Button type="text" icon={<ArrowUpOutlined style={{ color: '#007bff' }}/>}></Button>
               </Tooltip>
 
               <Tooltip title="上移">
@@ -74,7 +71,7 @@ export default function Income() {
           </div>
         </Slot>
         <Slot position="right">
-          <Button type="ghost" size="small" icon={<SettingOutlined />} className="pi-mg-right-3" style={{ color: '#007bff' }}>设置</Button>
+          <Button type="ghost" size="small" icon={<SettingOutlined />} className="pi-mg-right-3" style={{ color: '#007bff' }} onClick={() => setModalObj({ ...modalObj, type: 'setting', visible: true })}>设置</Button>
           <Button type="primary" size="small" onClick={() => setModalObj({ ...modalObj, type: 'add', visible: true })}>新建收入合同</Button>
         </Slot>
       </Header>

+ 5 - 11
src/pages/Contract/List/index.tsx

@@ -7,13 +7,14 @@ import { CaretDownOutlined } from '@ant-design/icons'
 import { Button, Dropdown, Menu, message, Table } from 'antd'
 import { ColumnsType } from 'antd/lib/table'
 import React, { useEffect, useState } from 'react'
-import { useActivate } from 'react-activation'
-import { RouteComponentProps } from 'react-router'
 import { Link } from 'react-router-dom'
 import { apiContractList } from './api'
 import styles from './index.module.scss'
 import './index.scss'
-const List: React.FC<RouteComponentProps> = () => {
+const List: React.FC<{}> = () => {
+  useEffect(() => {
+    getTree()
+  }, [])
   const [ tree, setTree ] = useState({
     bidsectionId: '',
     children: [],
@@ -36,7 +37,7 @@ const List: React.FC<RouteComponentProps> = () => {
     projectId: ''
   })
   const getTree = async () => {
-    const { data = {}, code = -1 } = await apiContractList()
+    const { data, code = -1 } = await apiContractList()
     if (code === consts.RET_CODE.SUCCESS) {
       setTree(data)
     }
@@ -103,13 +104,6 @@ const List: React.FC<RouteComponentProps> = () => {
     </Menu>
   )
 
-  useActivate(() => {
-    getTree()
-  })
-
-  useEffect(() => {
-    getTree()
-  }, [])
   return (
     <div className="list-content">
       <Header>

+ 4 - 1
src/router/routes.ts

@@ -43,7 +43,10 @@ export const routeConfig: RouteModel[] = [
             path: 'list',
             component: AsyncModuleLoader(() => import('@/pages/Contract/List')),
             defaultChildRoute: true,
-            auth: [ 'USER', 'ADMIN' ]
+            auth: [ 'USER', 'ADMIN' ],
+            meta: {
+              noCache: true
+            }
           },
           {
             path: 'content',

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 2 - 0
src/utils/sheets.ts


+ 4 - 1
src/utils/util.ts

@@ -77,7 +77,10 @@ const combinationPath = (parentPath: string|undefined, pathOfTargetConfig: strin
   return combinedPath
 }
 
-
+/**
+ * 日期格式化
+ * @param
+ */
 export {
   getCookie,
   storage,