Browse Source

项目日志页面

Renxy 1 year ago
parent
commit
30c52de7e9

+ 8 - 0
config/router.config.js

@@ -44,10 +44,18 @@ export default [
             path: '/home/approval/list',
             component: './PurchaseAdmin/PurchaseList/Approval/List',
           },
+          {
+            path: '/home/record',
+            component: './PurchaseAdmin/PurchaseList/DailyRecord',
+          },
           {
             path: '/home/approval/auth',
             component: './PurchaseAdmin/PurchaseList/Approval/Auth',
           },
+          {
+            path: '/home/approval/statistic',
+            component: './PurchaseAdmin/PurchaseList/Approval/Statistic',
+          },
           {
             path: '/home/demo',
             component: './PurchaseAdmin/PurchaseList/Report/Demo',

+ 40 - 7
src/pages/PurchaseAdmin/PurchaseList/Approval/DetailModal.js

@@ -1,5 +1,5 @@
-import React, { useState, useEffect, useMemo } from 'react';
-import { Form, Modal, Steps, Tabs } from 'antd';
+import React, { useState, useEffect, useMemo, useRef } from 'react';
+import { Form, Modal, Steps, Tabs, TreeSelect } from 'antd';
 import styles from './DetailModal.less';
 import TableRender from './TableRender';
 import MemberModal from './MemberModal';
@@ -8,7 +8,18 @@ import StatusRender from './StatusRender';
 const { Step } = Steps;
 // 新建
 function DetailModal(props) {
-  const { visible, onClose, onOk, form, data, flowList = [], disabled, loading } = props;
+  const {
+    visible,
+    onClose,
+    depUserTree,
+    onOk,
+    form,
+    data,
+    isEdit,
+    flowList = [],
+    disabled,
+    loading,
+  } = props;
   const [codes, setCodes] = useState({
     type: '',
     industry: '',
@@ -16,6 +27,9 @@ function DetailModal(props) {
     name: '',
     version: '',
   });
+  const [params, setParams] = useState({
+    sub_status: data.sub_status,
+  });
 
   useEffect(() => {
     if (!visible || !data.id) return;
@@ -59,10 +73,25 @@ function DetailModal(props) {
             </Form.Item>
           </>
         )}
-        {data.AuthorUser && (
+        {!isEdit && data.AuthorUser ? (
           <Form.Item className={styles.formItem} label="售前项目经理">
             {data.AuthorUser.CName}
           </Form.Item>
+        ) : (
+          <Form.Item label="售前项目经理" className={styles.formItem} name="managerID">
+            <TreeSelect
+              defaultValue={data.AuthorUser?.CName}
+              showSearch
+              allowClear
+              style={{ width: '60%' }}
+              placeholder="请选择项目经理"
+              multiple={false}
+              filterTreeNode={(input, option) => {
+                return option.props.title === input;
+              }}
+              treeData={depUserTree}
+            />
+          </Form.Item>
         )}
         {data.AuthorDepInfo && (
           <Form.Item className={styles.formItem} label="所属部门">
@@ -84,6 +113,12 @@ function DetailModal(props) {
             {data.OptManager.CName}
           </Form.Item>
         )}
+        {/* <Form.Item className={styles.formItem} label="项目阶段">
+          {data.OptManager.CName}
+        </Form.Item>
+        <Form.Item className={styles.formItem} label="现阶段状态">
+          {data.OptManager.CName}
+        </Form.Item> */}
         <Form.Item className={styles.formItem} label="项目规模">
           <TableRender
             onlyShow={true}
@@ -119,8 +154,6 @@ function DetailModal(props) {
     }
   };
 
-  console.log(flow.Nodes);
-
   const renderAuth = () => (
     <div className={styles.authDetail}>
       <div className={styles.subTitle}>审核详情</div>
@@ -138,7 +171,7 @@ function DetailModal(props) {
       {/* {data.audit_status != 0 && renderAuth()} */}
       <Tabs defaultActiveKey="3">
         <Tabs.TabPane tab="成员管理" key="1">
-          <MemberModal currentItem={data} />
+          <MemberModal isEdit={isEdit} currentItem={data} />
         </Tabs.TabPane>
         <Tabs.TabPane tab="审核详情" key="2">
           {renderAuth()}

+ 106 - 77
src/pages/PurchaseAdmin/PurchaseList/Approval/List.js

@@ -24,14 +24,28 @@ import ModifyManagerModal from './ModifyManagerModal';
 import { connect } from 'dva';
 import FirmModal from './ManufacturerModal';
 import { queryCreaterList, saveMfr } from '@/services/manufacturer';
+import ProjectRecordModal from './ProjectRecordModal';
 
 const { Option } = Select;
-//状态
-const STATUS = [
+//项目阶段
+const SECTION = [
   { value: 0, label: '售前' },
-  { value: 1, label: '转执行' },
-  { value: 2, label: '转运营' },
-  { value: 3, label: '转质保' },
+  { value: 1, label: '执行' },
+  { value: 2, label: '运营' },
+  { value: 3, label: '质保' },
+];
+//现阶段状态
+const STATUS = [
+  { value: 0, label: '初步交流' },
+  { value: 1, label: '预算和方式' },
+  { value: 2, label: '招标' },
+  { value: 3, label: '中标' },
+  { value: 4, label: '执行' },
+  { value: 5, label: '运营' },
+  { value: 6, label: '质保' },
+  { value: 7, label: '放弃' },
+  { value: 8, label: '失败' },
+  { value: 9, label: '关闭' },
 ];
 
 function List(props) {
@@ -61,6 +75,8 @@ function List(props) {
   const [qualityOperate, setQualityOperate] = useState(0);
   const [modifyManagerVisible, setModifyManagerVisible] = useState(false);
   const [addFirmVisible, setAddFirmVisible] = useState(false);
+  const [recordVisible, setRecordVisible] = useState(false);
+  const [isEdit, setIsEdit] = useState(false);
   const columns = [
     {
       title: '项目编号',
@@ -73,80 +89,64 @@ function List(props) {
     {
       title: '客户',
       dataIndex: 'supplier_name',
-      render: () => '-',
     },
     {
       title: '规模',
-      render: () => '-',
+      dataIndex: 'process_info',
+      render: info => {
+        const str = '';
+        if (info) {
+          const data = JSON.parse(info) || [];
+          const list = data.filter(item => item.scale);
+          str = list.join('+');
+        }
+        return str;
+      },
     },
     {
-      title: '技术',
-      render: () => '-',
+      title: '工艺',
+      dataIndex: 'process_info',
+      render: info => {
+        const str = '';
+        if (info) {
+          const data = JSON.parse(info) || [];
+          const list = data.filter(item => item.type);
+          str = list.join('+');
+        }
+        return str;
+      },
     },
     {
-      title: '项目种类(前分类)',
-      render: () => '-',
+      title: '项目种类',
+      dataIndex: 'TypeInfo',
+      render: TypeInfo => (TypeInfo ? `${TypeInfo.name}(${TypeInfo.code})` : '-'),
     },
     {
-      title: '项目阶段(前流程)',
-      render: () => '-',
+      title: '项目阶段',
+      dataIndex: ['FlowInfo', 'name'],
     },
     {
-      title: '现阶段状态(前状态)',
-      render: () => '-',
+      title: '现阶段状态',
+      dataIndex: 'project_status',
+      render: project_status => {
+        // return project_status === 0 ? <>售前</> : <>转执行</>;
+        //若添加其他状态则启用以下switch case:
+        switch (project_status) {
+          case 0:
+            return <>售前</>;
+          case 1:
+            return <>转执行</>;
+          case 2:
+            return <>转运营</>;
+          case 3:
+            return <>转质保</>;
+        }
+      },
     },
     {
       title: '现阶段状态时间(月)',
       render: () => '-',
     },
-    // {
-    //   title: '分类',
-    //   dataIndex: 'TypeInfo',
-    //   render: TypeInfo => (TypeInfo ? `${TypeInfo.name}(${TypeInfo.code})` : '-'),
-    // },
-    /*
-    {
-      title: '名称',
-      dataIndex: 'name',
-    },
-    {
-      title: '行业',
-      dataIndex: 'IndustryInfo',
-      render: IndustryInfo => `${IndustryInfo.name}(${IndustryInfo.code})`,
-    },
-    {
-      title: '所在地',
-      dataIndex: 'location',
-      render: (location, record) => `${location}(${record.location_code})`,
-    },
-    {
-      title: '期数',
-      dataIndex: 'version',
-      render: version => `${version}期`,
-    },
-    */
-    // {
-    //   title: '流程',
-    //   dataIndex: ['FlowInfo', 'name'],
-    // },
-    // {
-    //   title: '状态',
-    //   dataIndex: 'project_status',
-    //   render: project_status => {
-    //     // return project_status === 0 ? <>售前</> : <>转执行</>;
-    //     //若添加其他状态则启用以下switch case:
-    //     switch (project_status) {
-    //       case 0:
-    //         return <>售前</>;
-    //       case 1:
-    //         return <>转执行</>;
-    //       case 2:
-    //         return <>转运营</>;
-    //       case 3:
-    //         return <>转质保</>;
-    //     }
-    //   },
-    // },
     {
       title: '节点',
       dataIndex: 'NodeInfo',
@@ -194,7 +194,7 @@ function List(props) {
     },
     {
       title: '操作',
-      render: record => renderEditBtns2(record),
+      render: record => renderEditBtns(record),
     },
   ];
 
@@ -203,6 +203,7 @@ function List(props) {
       <>
         <a
           onClick={() => {
+            setIsEdit(false);
             setCurrentItem(record);
             setDetailVisible(true);
           }}
@@ -210,9 +211,24 @@ function List(props) {
           项目详情
         </a>
         <Divider type="vertical" />
-        <a onClick={() => {}}>项目编辑</a>
+        <a
+          onClick={() => {
+            setIsEdit(true);
+            setCurrentItem(record);
+            setDetailVisible(true);
+          }}
+        >
+          项目编辑
+        </a>
         <Divider type="vertical" />
-        <a onClick={() => {}}>项目日志</a>
+        <a
+          onClick={() => {
+            setCurrentItem(record);
+            setRecordVisible(true);
+          }}
+        >
+          项目日志
+        </a>
         <Divider type="vertical" />
         <a onClick={() => {}}>设置人日预算</a>
       </>
@@ -259,10 +275,24 @@ function List(props) {
         <Form.Item label="项目编号" name="projectCode">
           <Input style={{ width: 200 }} />
         </Form.Item>
-        <Form.Item label="状态" name="projectStatus">
+        <Form.Item label="项目阶段" name="projectStatus">
+          <Select
+            showSearch
+            style={{ width: 160 }}
+            filterOption={(input, option) =>
+              option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0
+            }
+          >
+            <Option value={null}>全部</Option>
+            {SECTION.map(item => (
+              <Option key={item.value}>{item.label}</Option>
+            ))}
+          </Select>
+        </Form.Item>
+        <Form.Item label="现阶段状态" name="projectStatus">
           <Select
             showSearch
-            style={{ width: 120 }}
+            style={{ width: 160 }}
             filterOption={(input, option) =>
               option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0
             }
@@ -615,11 +645,13 @@ function List(props) {
         onAddFirm={() => setAddFirmVisible(true)}
       />
       <DetailModal
+        depUserTree={depUserTree}
         industryList={industryList}
         flowList={flowList}
         typeList={typeList}
         visible={detailVisible}
         data={currentItem}
+        isEdit={isEdit}
         onClose={() => setDetailVisible(false)}
       />
       <ExecutionModal
@@ -630,14 +662,6 @@ function List(props) {
         onOk={() => setExecutionVisible(false)}
         onClose={() => setExecutionVisible(false)}
       />
-      {/* <MemberModal
-        depUserTree={depUserTree}
-        loading={loading}
-        visible={memberVisible}
-        onClose={() => setMemberVisible(false)}
-        currentItem={currentItem}
-        dataSource={member}
-      /> */}
       <QualityOperateModal
         depUserTree={depUserTree}
         loading={loading}
@@ -665,11 +689,16 @@ function List(props) {
         onOk={() => setModifyManagerVisible(false)}
       />
       <FirmModal
-        // projectId={projectId}
         visible={addFirmVisible}
         onCancel={() => setAddFirmVisible(false)}
         onOk={handlerSaveFirm}
       />
+      <ProjectRecordModal
+        depUserTree={depUserTree}
+        currentItem={currentItem}
+        visible={recordVisible}
+        onClose={() => setRecordVisible(false)}
+      />
     </div>
   );
 }

+ 34 - 31
src/pages/PurchaseAdmin/PurchaseList/Approval/MemberModal.js

@@ -16,6 +16,7 @@ function MemberRender(props) {
   const {
     visible,
     onClose,
+    isEdit = false,
     currentItem,
     loading,
     depUserTree,
@@ -107,38 +108,40 @@ function MemberRender(props) {
             )
         )}
       </Tabs>
-      <Form
-        labelCol={{ span: 6 }}
-        wrapperCol={{ span: 10 }}
-        width="100%"
-        style={{ marginBottom: 20 }}
-        layout="inline"
-        form={form}
-      >
-        <Form.Item
-          label="添加成员"
-          name="memberID"
-          initialValue={null}
-          rules={[{ required: true, message: '请选择成员' }]}
+      {isEdit && (
+        <Form
+          labelCol={{ span: 6 }}
+          wrapperCol={{ span: 10 }}
+          width="100%"
+          style={{ marginBottom: 20 }}
+          layout="inline"
+          form={form}
         >
-          <TreeSelect
-            showSearch
-            allowClear
-            style={{ width: 240 }}
-            placeholder="请选择项目成员"
-            multiple={false}
-            treeData={depUserTree}
-            filterTreeNode={(input, option) => {
-              return option.props.title === input;
-            }}
-          />
-        </Form.Item>
-        <Form.Item>
-          <Button type="primary" loading={loading} onClick={handleAddMember}>
-            添加
-          </Button>
-        </Form.Item>
-      </Form>
+          <Form.Item
+            label="添加成员"
+            name="memberID"
+            initialValue={null}
+            rules={[{ required: true, message: '请选择成员' }]}
+          >
+            <TreeSelect
+              showSearch
+              allowClear
+              style={{ width: 240 }}
+              placeholder="请选择项目成员"
+              multiple={false}
+              treeData={depUserTree}
+              filterTreeNode={(input, option) => {
+                return option.props.title === input;
+              }}
+            />
+          </Form.Item>
+          <Form.Item>
+            <Button type="primary" loading={loading} onClick={handleAddMember}>
+              添加
+            </Button>
+          </Form.Item>
+        </Form>
+      )}
       <Table columns={columns} loading={loading} dataSource={dataSource} pagination={false} />
     </>
   );

+ 84 - 0
src/pages/PurchaseAdmin/PurchaseList/Approval/ProjectRecordModal.js

@@ -0,0 +1,84 @@
+import { Button, DatePicker, Form, Modal, Table, TreeSelect } from 'antd';
+import styles from './index.less';
+const { RangePicker } = DatePicker;
+const ProjectRecordModal = ({ visible, depUserTree, onOk, onClose }) => {
+  const [form] = Form.useForm();
+  const columns = [
+    {
+      title: '提交人',
+      dataIndex: 'name',
+      width: '12%',
+    },
+    {
+      title: '日志标题',
+      dataIndex: 'title',
+      width: '20%',
+    },
+    {
+      title: '日志概述',
+      dataIndex: 'doc',
+      render: doc => <div className={styles.doc}>{doc}</div>,
+    },
+    {
+      title: '提交时间',
+      dataIndex: 'time',
+      width: '20%',
+    },
+    {
+      title: '操作',
+      width: '12%',
+      render: () => <a>详情</a>,
+    },
+  ];
+
+  const dataSource = [
+    {
+      name: '管理员',
+      title: '管理员的金科环境项目日志',
+      time: '1993-49-49 22:23:99',
+      doc:
+        '十八大不愧是你打卡是你打卡是你打卡机阿萨你打看那看你打卡是你打卡机阿萨你打看那看你打卡机阿萨你打看那看手打',
+    },
+  ];
+  return (
+    <Modal
+      title="日志详情"
+      open={visible}
+      width={1200}
+      onOk={onOk}
+      onCancel={onClose}
+      footer={null}
+    >
+      <Form
+        labelCol={{ span: 6 }}
+        wrapperCol={{ span: 18 }}
+        width="100%"
+        layout="inline"
+        style={{ marginBottom: 20 }}
+        form={form}
+      >
+        <Form.Item label="提交人:">
+          <TreeSelect
+            showSearch
+            allowClear
+            style={{ width: 240 }}
+            placeholder="请选择提交人"
+            multiple={false}
+            treeData={depUserTree}
+            filterTreeNode={(input, option) => {
+              return option.props.title === input;
+            }}
+          />
+        </Form.Item>
+        <Form.Item label="时间:">
+          <RangePicker style={{ width: 240 }} />
+        </Form.Item>
+        <Form.Item wrapperCol={{ span: 10, offset: 14 }}>
+          <Button type="primary">查询</Button>
+        </Form.Item>
+      </Form>
+      <Table columns={columns} dataSource={dataSource} />
+    </Modal>
+  );
+};
+export default ProjectRecordModal;

+ 25 - 0
src/pages/PurchaseAdmin/PurchaseList/Approval/Statistic.js

@@ -0,0 +1,25 @@
+import styles from './index.less';
+const Statistic = () => {
+  return (
+    <div className={styles.statistic}>
+      <div className={styles.boxCon}>
+        <div style={{ fontSize: '22px' }}>项目统计</div>
+        <div style={{ display: 'flex', width: '100%', justifyContent: 'space-around' }}>
+          <div style={{ textAlign: 'center' }}>
+            <div style={{ color: 'red', fontSize: '32px' }}>110</div>
+            <div>大部分看开点</div>
+          </div>
+          <div style={{ textAlign: 'center' }}>
+            <div style={{ color: 'red', fontSize: '32px' }}>110</div>
+            <div>大部分看开点</div>
+          </div>
+          <div style={{ textAlign: 'center' }}>
+            <div style={{ color: 'red', fontSize: '32px' }}>110</div>
+            <div>大部分看开点</div>
+          </div>
+        </div>
+      </div>
+    </div>
+  );
+};
+export default Statistic;

+ 3 - 32
src/pages/PurchaseAdmin/PurchaseList/Approval/StatusRender.js

@@ -1,40 +1,11 @@
 import { Timeline } from 'antd';
+import styles from './index.less';
 
 const StatusRender = () => {
-  const renderIcon = idx => {
-    <div
-      style={{
-        backgroundColor: '#1890ff',
-        color: '#fff',
-        width: '30px',
-        height: '30px',
-        textAlign: 'center',
-        lineHeight: '30px',
-      }}
-    >
-      {idx}
-    </div>;
-  };
   return (
     <Timeline>
-      <Timeline.Item
-        dot={
-          <div
-            style={{
-              backgroundColor: '#1890ff',
-              color: '#fff',
-              width: '30px',
-              height: '30px',
-              textAlign: 'center',
-              lineHeight: '30px',
-              borderRadius: '15px',
-            }}
-          >
-            1
-          </div>
-        }
-      >
-        <div style={{ fontSize: '20px', color: '#1890ff' }}>售前经理</div>
+      <Timeline.Item dot={<div className={styles.icon}>1</div>}>
+        <div style={{ fontSize: '16px', color: '#1890ff' }}>售前经理</div>
         <div>售前Solve initial network problems 2015-09-01经理</div>
       </Timeline.Item>
       <Timeline.Item>Solve initial network problems 2015-09-01</Timeline.Item>

+ 43 - 38
src/pages/PurchaseAdmin/PurchaseList/Approval/TableRender.js

@@ -33,51 +33,56 @@ const TableRender = ({ value, onChange, onlyShow = false }) => {
     const { type, scale } = item;
     return (
       <div className={styles.item} key={`${type}_${scale}_${idx}`}>
-        {onlyShow ? (
-          <>
-            <span>{type}</span>
-            <div className={styles.line}></div>
-            <span>{scale}</span>
-          </>
-        ) : (
-          <>
-            <Select
-              value={type}
-              className={styles.itemLeft}
-              style={{ width: '100%' }}
-              onChange={e => handleChange(idx, { ...item, type: e })}
-            >
-              {options.map(value => (
-                <Option key={value}>{value}</Option>
-              ))}
-            </Select>
-            <Input
-              value={scale}
-              onChange={e => handleChange(idx, { ...item, scale: e.target.value })}
-            />
-
-            <DeleteOutlined className={styles.deleteIcon} onClick={() => handleDelClick(idx)} />
-          </>
-        )}
+        <Select
+          value={type}
+          className={styles.itemLeft}
+          style={{ width: '100%' }}
+          onChange={e => handleChange(idx, { ...item, type: e })}
+        >
+          {options.map(value => (
+            <Option key={value}>{value}</Option>
+          ))}
+        </Select>
+        <Input
+          value={scale}
+          onChange={e => handleChange(idx, { ...item, scale: e.target.value })}
+        />
+        <DeleteOutlined className={styles.deleteIcon} onClick={() => handleDelClick(idx)} />
+      </div>
+    );
+  };
+  const renderOnlyShowItems = (item, idx) => {
+    const { type, scale } = item;
+    return (
+      <div className={styles.item} key={`${type}_${scale}_${idx}`}>
+        <span>{type}</span>
+        <div className={styles.line}></div>
+        <span>{scale}</span>
       </div>
     );
   };
   return (
     <div>
-      {!onlyShow && (
-        <div className={styles.titleContent}>
-          <div>工艺技术</div>
-          <div>项目规模</div>
+      {onlyShow ? (
+        <div className={styles.content}>
+          {list.map((item, idx) => renderOnlyShowItems(item, idx))}
         </div>
-      )}
-      <div className={styles.content}>
-        {list.map((item, idx) => renderItems(item, idx))}
-        {!onlyShow && (
-          <div className={styles.addBtn} onClick={handleAddClick}>
-            + 添加
+      ) : (
+        <>
+          <div className={styles.titleContent}>
+            <div>工艺技术</div>
+            <div>项目规模</div>
           </div>
-        )}
-      </div>
+
+          <div className={styles.content}>
+            {list.map((item, idx) => renderItems(item, idx))}
+
+            <div className={styles.addBtn} onClick={handleAddClick}>
+              + 添加
+            </div>
+          </div>
+        </>
+      )}
     </div>
   );
 };

+ 26 - 1
src/pages/PurchaseAdmin/PurchaseList/Approval/index.less

@@ -45,6 +45,31 @@
     }
   }
 }
-.content:last-child {
+
+.item:last-child {
   border-bottom: none;
 }
+.icon {
+  background-color: #1890ff;
+  color: #fff;
+  width: 24px;
+  height: 24px;
+  text-align: center;
+  line-height: 24px;
+  border-radius: 12px;
+}
+.doc {
+  width: 400px;
+  white-space: nowrap; /* 防止文本换行 */
+  overflow: hidden; /* 隐藏溢出的文本 */
+  text-overflow: ellipsis; /* 显示省略号 */
+}
+.statistic {
+  padding: 40px;
+  .boxCon {
+    width: 100%;
+    padding: 10px;
+    border-radius: 12px;
+    box-shadow: 0px 0px 20px 4px rgba(0, 0, 0, 0.3);
+  }
+}

+ 39 - 0
src/pages/PurchaseAdmin/PurchaseList/DailyRecord/components/RecordDetail.js

@@ -0,0 +1,39 @@
+import { Button, Col, Form, Input, Modal, Row, Select, Space } from 'antd';
+import styles from './index.less';
+import { useEffect, useState } from 'react';
+import { DeleteOutlined } from '@ant-design/icons';
+
+const RecordDetailModal = ({ visible, user, projects, loading = false, onOk, onCancel }) => {
+  const RenderItem = item => {
+    return (
+      <div className={styles.itemCon}>
+        <div>项目名称:那地方那看来你付款烂了放哪</div>
+        <div>项目名称:那地方那看来你付款烂了放哪</div>
+        <div>
+          项目名称:那地方那看来你付款烂了放哪那地方那看来你付款烂了放哪那地方那看来你付款烂了放哪那地方那看来你付款烂了放哪
+        </div>
+      </div>
+    );
+  };
+
+  return (
+    <Modal
+      title="日志详情"
+      open={visible}
+      width={800}
+      onOk={onOk}
+      onCancel={onCancel}
+      footer={null}
+    >
+      <div className={styles.titleContent}>
+        <span className={styles.title}>管理员的金科环境项目日志</span>
+        <span>1999-99-99 09:90</span>
+      </div>
+      <Space direction="vertical" size={16} className={styles.detailContent}>
+        {[1, 2, 3].map(item => RenderItem())}
+      </Space>
+    </Modal>
+  );
+};
+
+export default RecordDetailModal;

+ 124 - 0
src/pages/PurchaseAdmin/PurchaseList/DailyRecord/components/WriteRecordModal.js

@@ -0,0 +1,124 @@
+import { Button, Col, Form, Input, Modal, Row, Select } from 'antd';
+import styles from './index.less';
+import { useEffect, useState } from 'react';
+import { DeleteOutlined } from '@ant-design/icons';
+
+const WriteRecordModal = ({ visible, user, projects, loading = false, onOk, onCancel }) => {
+  const [form] = Form.useForm();
+  const defaultData = { projectName: '', tip: '', doc: '' };
+  const [list, setList] = useState([defaultData]);
+  console.log(user);
+  useEffect(() => {
+    if (!visible) setList([]);
+  }, [visible]);
+
+  const handleAddClick = () => {
+    setList([...list, defaultData]);
+  };
+
+  const handleChangeItem = (idx, item) => {
+    const newList = [...list];
+    newList[idx] = item;
+    setList(newList);
+  };
+
+  const handleDelItem = idx => {
+    if (list.length <= 1) return;
+    const newList = [...list];
+    newList.splice(idx, 1);
+    setList(newList);
+  };
+  return (
+    <Modal title="写日志" open={visible} width={800} onOk={onOk} onCancel={onCancel}>
+      <Form
+        labelCol={{ span: 4 }}
+        wrapperCol={{ span: 18 }}
+        width="100%"
+        style={{ marginBottom: 20 }}
+        form={form}
+      >
+        <Form.Item
+          label="日志标题"
+          name="memberID"
+          initialValue={`${user.CName}的金科环境项目日志`}
+          rules={[{ required: true, message: '请选择成员' }]}
+        >
+          <Input />
+        </Form.Item>
+        <Form.Item
+          label="日志详情"
+          name="memberID"
+          initialValue={'管理员的金科环境项目日志'}
+          rules={[{ required: true, message: '请选择成员' }]}
+        >
+          {list.map((item, idx) => (
+            <RenderItem
+              key={`${item.projectName}_${idx}`}
+              idx={idx}
+              projects={projects}
+              data={item}
+              onChange={handleChangeItem}
+              onDelete={handleDelItem}
+            />
+          ))}
+        </Form.Item>
+
+        <Form.Item wrapperCol={{ span: 20, offset: 4 }}>
+          <Button style={{ width: '100px' }} onClick={handleAddClick}>
+            + 新增
+          </Button>
+        </Form.Item>
+      </Form>
+    </Modal>
+  );
+};
+
+export default WriteRecordModal;
+
+const RenderItem = ({ idx, data, projects, onChange, onDelete }) => {
+  const [form] = Form.useForm();
+  return (
+    <div className={styles.itemContent}>
+      <Form labelCol={{ span: 7 }} wrapperCol={{ span: 17 }} width="100%" form={form}>
+        <Row>
+          <Col span={12}>
+            <Form.Item
+              label="项目名称"
+              name="projectName"
+              initialValue={data.projectName}
+              rules={[{ required: true, message: '请选择项目' }]}
+            >
+              <Select
+                // style={{ width: 120 }}
+                options={projects?.map(item => {
+                  return { label: item.Name, value: item.ID };
+                })}
+              />
+            </Form.Item>
+          </Col>
+          <Col span={11} offset={1}>
+            <Form.Item
+              label="日志概述"
+              name="tip"
+              initialValue={data.tip}
+              rules={[{ required: true, message: '请选择成员' }]}
+            >
+              <Input />
+            </Form.Item>
+          </Col>
+        </Row>
+        <Form.Item
+          style={{ marginBottom: 0 }}
+          labelCol={{ span: 3 }}
+          wrapperCol={{ span: 21 }}
+          label="日志详情"
+          initialValue={data.doc}
+          name="doc"
+        >
+          <Input.TextArea style={{ height: 120 }} />
+        </Form.Item>
+      </Form>
+      <DeleteOutlined className={styles.delIcon} onClick={idx => onDelete(idx)} />
+    </div>
+  );
+};

+ 39 - 0
src/pages/PurchaseAdmin/PurchaseList/DailyRecord/components/index.less

@@ -0,0 +1,39 @@
+.itemContent {
+  position: relative;
+  border: 1px dashed #eee;
+  padding: 10px;
+  margin-bottom: 16px;
+  :global {
+    .ant-col-3 {
+      flex: 0 0 14.5%;
+      max-width: 14.5%;
+    }
+  }
+  .delIcon {
+    position: absolute;
+    top: calc(50% - 15px);
+    right: -30px;
+    font-size: 20px;
+  }
+}
+.itemContent:last-child {
+  margin-bottom: none;
+}
+
+.titleContent {
+  display: flex;
+  justify-content: space-between;
+  margin-bottom: 24px;
+  .title {
+    font-size: 24px;
+    color: #599ae4;
+  }
+}
+.detailContent {
+  .itemCon {
+    width: 100%;
+    padding: 10px;
+    border-radius: 8px;
+    box-shadow: 0px 0px 6px 1px rgba(0, 0, 0, 0.3);
+  }
+}

+ 94 - 0
src/pages/PurchaseAdmin/PurchaseList/DailyRecord/index.js

@@ -0,0 +1,94 @@
+import { Button, DatePicker, Space, Table } from 'antd';
+import { useEffect, useState } from 'react';
+import WriteRecordModal from './components/WriteRecordModal';
+import { connect } from 'dva';
+import RecordDetailModal from './components/RecordDetail';
+import styles from './index.less';
+
+const DailyRecord = props => {
+  const { currentUser, projects, dispatch } = props;
+  const [date, setDate] = useState('');
+  const [visible, setVisible] = useState(false);
+  const [detailOpen, setDetailOpen] = useState(true);
+
+  const columns = [
+    {
+      title: '日志标题',
+      dataIndex: 'title',
+      width: '20%',
+    },
+    {
+      title: '日志概述',
+      dataIndex: 'doc',
+      render: doc => <div className={styles.doc}>{doc}</div>,
+    },
+    {
+      title: '提交人',
+      dataIndex: 'name',
+      width: '16%',
+    },
+    {
+      title: '提交时间',
+      dataIndex: 'time',
+      width: '20%',
+    },
+    {
+      title: '操作',
+      width: '20%',
+      render: () => (
+        <Space>
+          <a>编辑</a>
+          <a onClick={() => setDetailOpen(true)}>详情</a>
+          <a>删除</a>
+        </Space>
+      ),
+    },
+  ];
+
+  const dataSource = [
+    {
+      name: '管理员',
+      title: '管理员的金科环境项目日志',
+      time: '1993-49-49 22:23:99',
+      doc:
+        '十八大不愧是你打卡是你打八大不愧是你打卡是你打卡机阿萨你打看那看手八大不愧是你打卡是你打卡机阿萨你打看那看手八大不愧是你打卡是你打卡机阿萨你打看那看手八大不愧是你打卡是你打卡机阿萨你打看那看手卡机阿萨你打看那看手打',
+    },
+  ];
+
+  useEffect(() => {
+    dispatch({ type: 'record/queryProject' });
+  }, []);
+
+  const onChange = (date, dateString) => {
+    console.log(date, dateString);
+  };
+  return (
+    <div>
+      <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: '20px' }}>
+        <Space size={24}>
+          <DatePicker onChange={onChange} />
+          <Button type="primary" onClick={() => {}}>
+            查询
+          </Button>
+        </Space>
+        <Button type="primary" onClick={() => setVisible(true)}>
+          写日志
+        </Button>
+      </div>
+      <Table columns={columns} dataSource={dataSource} />
+      <WriteRecordModal
+        visible={visible}
+        user={currentUser}
+        projects={projects}
+        onOk={() => {}}
+        onCancel={() => setVisible(false)}
+      />
+      <RecordDetailModal visible={detailOpen} onCancel={() => setDetailOpen(false)} />
+    </div>
+  );
+};
+
+export default connect(({ user, record, loading }) => ({
+  currentUser: user.currentUser,
+  projects: record.projects,
+}))(DailyRecord);

+ 6 - 0
src/pages/PurchaseAdmin/PurchaseList/DailyRecord/index.less

@@ -0,0 +1,6 @@
+.doc {
+  width: 700px;
+  white-space: nowrap; /* 防止文本换行 */
+  overflow: hidden; /* 隐藏溢出的文本 */
+  text-overflow: ellipsis; /* 显示省略号 */
+}

+ 82 - 0
src/pages/PurchaseAdmin/PurchaseList/DailyRecord/models/record.js

@@ -0,0 +1,82 @@
+import {
+  queryWorkType,
+  queryWorkHours,
+  queryAuthWorkHours,
+  addWorkHours,
+  addAuthWorkHours,
+  authWorkload,
+  queryProject,
+  deleteWorkHour,
+} from '@/services/workHours';
+import { queryDepV2 } from '@/services/approval';
+import { message } from 'antd';
+import moment from 'moment';
+
+function getDepUserTree(data, map) {
+  data.title = `${data.Name}`;
+  data.key = `${data.ID}`;
+  data.value = `${data.ID}`;
+  map.set(data.ID, data);
+  if (!data.children) data.children = new Array();
+
+  if (data.children) {
+    data.children.forEach(item => {
+      getDepUserTree(item, map);
+    });
+  }
+  return data;
+}
+
+export default {
+  namespace: 'record',
+  state: {
+    subTypeList: [],
+    projects: [],
+    depUserTree: [],
+  },
+
+  effects: {
+    *querySubType({ payload, callback }, { call, put }) {
+      const { data } = yield call(queryWorkType, payload);
+      callback && callback(data);
+      yield put({
+        type: 'save',
+        payload: { subTypeList: data },
+      });
+    },
+    *queryProject({ payload = {}, callback }, { call, put }) {
+      const res = yield call(queryProject, payload);
+      if (res) {
+        yield put({
+          type: 'save',
+          payload: {
+            projects: res.data.list,
+          },
+        });
+        callback && callback();
+      }
+    },
+    *fetchDepV2({ payload, callback }, { call, put }) {
+      const response = yield call(queryDepV2, { pageSize: 999999 });
+      if (response) {
+        const depUserMap = new Map();
+        const depUserTree = response.data.list.map(item => {
+          return getDepUserTree(item, depUserMap);
+        });
+        yield put({
+          type: 'save',
+          payload: { depUserTree, depUserMap },
+        });
+      }
+    },
+  },
+
+  reducers: {
+    save(state, action) {
+      return {
+        ...state,
+        ...action.payload,
+      };
+    },
+  },
+};

+ 6 - 0
src/pages/PurchaseAdmin/PurchaseList/Index.js

@@ -67,7 +67,13 @@ function LayoutDetail(props) {
                 <Menu.Item key="/home/approval/auth">
                   <Link to="/home/approval/auth">审核列表</Link>
                 </Menu.Item>
+                <Menu.Item key="/home/approval/statistic">
+                  <Link to="/home/approval/statistic">统计分析</Link>
+                </Menu.Item>
               </SubMenu>
+              <Menu.Item key="/home/record">
+                <Link to="/home/record">项目日志</Link>
+              </Menu.Item>
 
               {checkReport(0) && (
                 <SubMenu key="/home/report" title="工时报表">