Explorar o código

Merge branch 'renyicun'

任一存 hai 1 ano
pai
achega
aeac13a677

+ 2 - 0
.vscode/settings.json

@@ -0,0 +1,2 @@
+{
+}

+ 5 - 1
README.md

@@ -50,4 +50,8 @@ mySelect:SelectType
 
 
 3.插件问题
-npm --registry https://registry.npmjs.org/ install react-file-viewer --force
+npm --registry https://registry.npmjs.org/ install react-file-viewer --force
+
+4.蓝湖
+地址:https://lanhuapp.com/link/#/invite?sid=qXnODvNa
+密码:8Hn3

+ 2 - 0
package.json

@@ -4,6 +4,7 @@
   "private": true,
   "dependencies": {
     "@ant-design/cssinjs": "^1.5.6",
+    "@babel/plugin-proposal-private-property-in-object": "^7.21.11",
     "@testing-library/jest-dom": "^5.16.5",
     "@testing-library/react": "^13.4.0",
     "@testing-library/user-event": "^13.5.0",
@@ -27,6 +28,7 @@
     "redux-devtools-extension": "^2.13.9",
     "redux-thunk": "^2.4.1",
     "sass": "^1.55.0",
+    "sortablejs": "^1.15.2",
     "typescript": "^4.8.4",
     "web-vitals": "^2.1.4"
   },

+ 107 - 74
src/pages/A1Project/A1Add/index.module.scss

@@ -9,92 +9,128 @@
   border-radius: 10px;
 
   :global {
-
     .A1AddMain {
       width: 100%;
-      height: calc(100% - 60px);
-      overflow-y: auto;
-      overflow-x: hidden;
+      height: 100%;
       padding: 20px 20px 20px 0px;
       position: relative;
-
-      .ant-form-item-label {
-        width: 120px !important;
-      }
-
-      .A1AddBtn {
-        position: absolute;
-        left: 800px;
-        top: 50%;
-        transform: translateY(-50%);
-      }
-
-      .e_row {
-        padding-right: 50px;
-        display: flex;
-        font-size: 14px;
-        margin-bottom: 10px;
-
-        .e_rowL {
-          width: 120px;
-          text-align: right;
-
-          &>span {
-            color: #ff4d4f;
-            position: relative;
-            top: 3px;
+      padding-bottom: 80px;
+      .ant-form {
+        width: 100%;
+        height: 100%;
+        overflow-y: auto;
+        overflow-x: hidden;
+        .form-item-group-one-row {
+          display: flex;
+          align-items: center;
+          gap: 20px;
+          >div {
+            flex: 1 0 auto;
+            max-width: 400px;
+            margin-bottom: 24px;
           }
         }
-
-        .e_rowL2 {
-          position: relative;
-          top: 4px;
-
-
+  
+        .form-item-group-title__porject-type{
+          margin-left: 50px;
+          margin-bottom: 10px;
         }
-
-        .e_rowR {
-          width: calc(100% - 130px);
-
-          .lookNoneOne {
+        .form-item-group__porject-type {
+          padding-top: 20px;
+          padding-right: 20px;
+          margin-left: 50px;
+          border: 1px solid #d9d9d9;
+          border-radius: 5px;
+          background-color: rgba(0, 0, 0, 0.05);
+          width: fit-content;
+        }
+        
+        .ant-form-item-label {
+          width: 120px !important;
+        }
+  
+        .e_row {
+          padding-right: 50px;
+          display: flex;
+          font-size: 14px;
+          margin-bottom: 10px;
+  
+          .e_rowL {
+            width: 120px;
+            text-align: right;
+  
+            &>span {
+              color: #ff4d4f;
+              position: relative;
+              top: 3px;
+            }
+          }
+  
+          .e_rowL2 {
             position: relative;
-            left: 10px;
             top: 4px;
+  
+  
           }
-
-          .erLinkTop {
-            .ant-btn {
-              width: 82px;
+  
+          .e_rowR {
+            width: calc(100% - 130px);
+  
+            .lookNoneOne {
+              position: relative;
+              left: 10px;
+              top: 4px;
             }
-
-            .e_rowRtit {
-              font-size: 14px;
-              color: rgb(126, 124, 124);
+  
+            .erLinkTop {
+              .ant-btn {
+                width: 82px;
+              }
+  
+              .e_rowRtit {
+                font-size: 14px;
+                color: rgb(126, 124, 124);
+              }
             }
-          }
-
-          .erLinkMain {
-            .erLinkRow {
-              margin-top: 15px;
+  
+            .erLinkMain {
+              .erLinkRow {
+                margin-top: 15px;
+              }
             }
+  
+  
           }
-
-
+  
+        }
+  
+        .A1AtitTxt {
+          position: relative;
+          bottom:30px;
+          left: 120px;
+          opacity: 0;
+          transition: all .3s;
+          pointer-events: none;
+        }
+        .A1AtitTxtShow{
+          opacity: 1;
+          bottom: 24px;
+        }
+  
+        .A1AddBtn {
+          position: absolute;
+          bottom: 0;
+          left: 0;
+          height: 60px;
+          width: 100%;
+          padding-left: 50px;
+          border-top: 1px solid #d9d9d9;
+          background-color: #fff;
+          display: flex;
+          align-items: center;
+          gap: 20px;
+          box-shadow: 0px 0px 10px 0px rgba(0, 0, 0, 0.15);
         }
-
-      }
-
-      .A1AtitTxt {
-        position: relative;
-        bottom:30px;
-        left: 120px;
-        opacity: 0;
-        transition: all .3s;
-        pointer-events: none;
-      }
-      .A1AtitTxtShow{
-        opacity: 1;
-        bottom: 24px;
       }
     }
 
@@ -222,8 +258,5 @@
       }
 
     }
-
-
-
   }
 }

+ 152 - 6
src/pages/A1Project/A1Add/index.tsx

@@ -16,6 +16,8 @@ import {
   InputNumber,
   Popconfirm,
   Select,
+  Radio,
+  Checkbox,
 } from "antd";
 import mapDataAll from "./data";
 import dayjs from "dayjs";
@@ -64,6 +66,8 @@ function A1Add({ pageType, closeFu, addFu, editFu }: Props) {
         ...info,
         province,
         dateScope: dateScope ? [dayjs(dateScope[0]), dayjs(dateScope[1])] : "",
+        dictProjectBusinessIds: Number(info.dictProjectBusinessIds), // 提交表单时这一项数据类型是 number,获取表单数据时这一项数据类型是string。
+        bidDate: dayjs(info.bidDate),
       });
 
       // 回显的时候闪动 问题
@@ -75,26 +79,57 @@ function A1Add({ pageType, closeFu, addFu, editFu }: Props) {
     if (!pageType.id) {
       // 新增
       setIsOk(true);
-    } else {
-      // 编辑
+    } else if (pageType.txt === 'look') {
+      // 查看
       getInfoFu(pageType.id);
+    } else if (pageType.txt === 'edit') {
+      // 编辑
+      setIsBid(lookInfo.isBid);
     }
-  }, [getInfoFu, pageType.id]);
+  }, [getInfoFu, pageType.id, pageType.txt, lookInfo.isBid]);
 
   // 从仓库 获取 项目状态的下拉框 数据
   const statusArr = useSelector(
     (state: RootState) => state.A2Dict.A2Tab1_1Obj.status
   );
 
+  // 从仓库 获取 签订主体 数据
+  const mainArr = useSelector(
+    (state: RootState) => state.A2Dict.A2Tab1_1Obj.main
+  );
+  
+  // 从仓库 获取 业务部门 数据
+  const deptArr = useSelector(
+    (state: RootState) => state.A2Dict.A2Tab1_1Obj.dept
+  );
+
+  // 从仓库 获取 业务类型 数据
+  const projectBusinessArr = useSelector(
+    (state: RootState) => state.A2Dict.A2Tab1_1Obj.projectBusiness
+  );
+  
+  // 从仓库 获取 项目范围 数据
+  const projectScopeArr = useSelector(
+    (state: RootState) => state.A2Dict.A2Tab1_1Obj.projectScope
+  );
+
+  // 从仓库 获取 客户端 数据
+  const projectAppArr = useSelector(
+    (state: RootState) => state.A2Dict.A2Tab1_1Obj.projectApp
+  );
+  
   // 表单的ref
   const FormBoxRef = useRef<FormInstance>(null);
 
   // 没有通过校验
-  const onFinishFailed = useCallback(() => {}, []);
+  const onFinishFailed = useCallback(() => {
+    console.log("没有通过校验");
+  }, []);
 
   // 通过校验点击确定
   const onFinish = useCallback(
     async (value: any) => {
+      
       //  项目周期的处理
       let dateScope = "";
       if (value.dateScope && value.dateScope.length >= 1) {
@@ -109,12 +144,17 @@ function A1Add({ pageType, closeFu, addFu, editFu }: Props) {
       if (value.province && value.province.length)
         province = value.province.join("-");
 
+      // 中标日期的处理
+      let bidDate = "";
+      if (value.bidDate) bidDate = dayjs(value.bidDate).format("YYYY-MM-DD");
+      
       const obj = {
         ...value,
         id: pageType.txt === "add" ? null : pageType.id,
         dateScope,
         amount: value.amount ? value.amount : "",
         province,
+        bidDate,
       };
       const res = await A1_APIaddProject(obj);
       if (res.code === 0) {
@@ -148,6 +188,9 @@ function A1Add({ pageType, closeFu, addFu, editFu }: Props) {
   // 创建人 的 值
   const [creatorIdValue, setCreatorIdValue] = useState(userInfo.id);
 
+  // 是否投标
+  const [isBid, setIsBid] = useState<number | undefined>();
+
   return (
     <div className={styles.A1Add}>
       <div
@@ -167,6 +210,9 @@ function A1Add({ pageType, closeFu, addFu, editFu }: Props) {
             snapPmUser: userInfo.realName,
             creatorId: userInfo.id,
           }}
+          onValuesChange={(changedValues, allValues) => {
+            setIsBid(allValues.isBid);
+          }}
         >
           <Form.Item
             label="项目编号"
@@ -261,6 +307,59 @@ function A1Add({ pageType, closeFu, addFu, editFu }: Props) {
             />
           </Form.Item>
 
+          <Form.Item
+            label="签订主体"
+            name="dictMainId"
+          >
+            <Select
+              placeholder={
+                isOk ? (pageType.txt === "look" ? "(空)" : "请选择") : ""
+              }
+              style={{ width: 300 }}
+              options={mainArr.map((v) => ({ value: v.id, label: v.name }))}
+            />
+          </Form.Item>
+
+          <Form.Item
+            label="业务部门"
+            name="dictDeptId"
+          >
+            <Select
+              placeholder={
+                isOk ? (pageType.txt === "look" ? "(空)" : "请选择") : ""
+              }
+              style={{ width: 300 }}
+              options={deptArr.map((v) => ({ value: v.id, label: v.name }))}
+            />
+          </Form.Item>
+
+          <div className="form-item-group-one-row">
+            <Form.Item
+              label="是否投标"
+              name="isBid"
+            >
+              <Radio.Group>
+                <Radio value={0}>否</Radio>
+                <Radio value={1}>是</Radio>
+              </Radio.Group>
+            </Form.Item>
+            
+            {pageType.txt !== 'look' && isBid ? (
+              <Form.Item label="中标日期" name="bidDate"
+                required={isBid === 1}
+              >
+                <DatePicker
+                  style={{ width: 300 }}
+                  disabled={isBid === 0}
+                />
+              </Form.Item>
+            ) : (pageType.txt === "look" && lookInfo.isBid === 1) ? (
+                  <div>中标日期:{isOk ? lookInfo.bidDate : ""}</div>
+            ) : (
+              null
+            )}
+          </div>
+
           {pageType.txt === "look" ? (
             <div className="e_row">
               <div className="e_rowL">
@@ -346,6 +445,55 @@ function A1Add({ pageType, closeFu, addFu, editFu }: Props) {
             </div>
           )}
 
+          <h3 className="form-item-group-title__porject-type">项目类型</h3>
+          <div className="form-item-group__porject-type">
+            {/* 表单项:业务类型,单选。 */}
+            <Form.Item
+              label="业务类型"
+              name="dictProjectBusinessIds"
+              tooltip="甲方的业务类型"
+            >
+              <Radio.Group>
+                {projectBusinessArr.map((v) => (
+                  <Radio key={v.id} value={v.id}>
+                    <span title={v.description}>{v.name}</span>
+                  </Radio>
+                ))}
+              </Radio.Group>
+            </Form.Item>
+
+            {/* 表单项:项目范围,多选。 */}
+            <Form.Item
+              label="项目范围"
+              name="dictProjectScopeIds"
+              tooltip="项目的建设范围"
+            >
+              <Checkbox.Group>
+                {projectScopeArr.map((v) => (
+                  <Checkbox key={v.id} value={v.id}>
+                    <span title={v.description}>{v.name}</span>
+                  </Checkbox>
+                ))}
+              </Checkbox.Group>
+            </Form.Item>
+
+            {/* 表单项:客户端,多选。 */}
+            <Form.Item
+              label="客户端"
+              name="dictProjectAppIds"
+              tooltip="项目成果的呈现载体"
+            >
+              <Checkbox.Group>
+                {projectAppArr.map((v) => (
+                  <Checkbox key={v.id} value={v.id}>
+                    <span title={v.description}>{v.name}</span>
+                  </Checkbox>
+                ))}
+              </Checkbox.Group>
+            </Form.Item>
+
+          </div>
+
           {/* 确定和取消按钮 */}
           <div className="A1AddBtn">
             {pageType.txt === "look" ? null : (
@@ -359,8 +507,6 @@ function A1Add({ pageType, closeFu, addFu, editFu }: Props) {
                 >
                   <Button>取消</Button>
                 </Popconfirm>
-                <br />
-                <br />
                 <Button type="primary" htmlType="submit">
                   保存
                 </Button>

+ 23 - 2
src/pages/A1Project/index.module.scss

@@ -20,7 +20,17 @@
       .A1Search2 {
         width: 1267px;
         position: relative;
-
+        .projectTypeFilterButton {
+          display: inline-flex;
+          align-items: center;
+          >span:first-of-type {
+            display: inline-block;
+            max-width: 400px;
+            white-space: nowrap;
+            overflow: hidden;
+            text-overflow: ellipsis;
+          }
+        }
         .A1SearchBtn {
           position: absolute;
           right: 8px;
@@ -48,6 +58,17 @@
     }
 
   }
+}
 
-
+.projectTypePopOver {
+  >.row {
+    .title {
+      display: inline-block;
+      width: 5em;
+      font-weight: bold;
+    }
+    .selectAll {
+      font-weight: bold;
+    }
+  }
 }

+ 261 - 17
src/pages/A1Project/index.tsx

@@ -6,7 +6,8 @@ import React, {
   useState,
 } from "react";
 import styles from "./index.module.scss";
-import { Button, Input, Popconfirm, Select, Table, Tooltip } from "antd";
+import { Button, Input, Popconfirm, Select, Table, Tooltip, Popover, Radio, Checkbox } from "antd";
+import { DownOutlined } from '@ant-design/icons';
 import { useDispatch, useSelector } from "react-redux";
 import { RootState } from "@/store";
 import { A1TableType } from "@/types";
@@ -19,6 +20,10 @@ import { hasAuditStatusArr } from "./data";
 import { MessageFu } from "@/utils/message";
 import AuthCom from "@/components/AuthCom";
 
+import type { CheckboxProps, GetProp } from 'antd';
+
+type CheckboxValueType = GetProp<typeof Checkbox.Group, 'value'>[number];
+
 function A1Project() {
   const dispatch = useDispatch();
 
@@ -32,8 +37,8 @@ function A1Project() {
     (state: RootState) => state.A2Dict.A2Tab1_1Obj.status
   );
 
-  // 顶部的试图切换
-  const [topType, setTopType] = useState<"outer" | "inner">("outer");
+  // 顶部的试图切换。item: 项目文件视图,inside: 内控文件视图
+  const [topType, setTopType] = useState<"item" | "inside">("item");
 
   // 表单数据
   const [fromData, setFromData] = useState({
@@ -45,7 +50,10 @@ function A1Project() {
     projectRole: "",
     pageNum: 1,
     pageSize: 10,
-    type: "inside",
+    type: "item",
+    dictProjectBusinessIds: '', // 业务类型id
+    dictProjectScopeIds: '', // 项目范围id
+    dictProjectAppIds: '', // 客户端id
   });
 
   // 封装发送请求的函数
@@ -81,6 +89,9 @@ function A1Project() {
   const resetSelectFu = useCallback(() => {
     // 把2个输入框和时间选择器清空
     setInputKey(Date.now());
+    setCheckedProjectBusinessArr([])
+    setCheckedProjectScopeArr([])
+    setCheckedProjectAppArr([])
     setFromData({
       searchKey: "",
       pmName: "",
@@ -90,32 +101,196 @@ function A1Project() {
       projectRole: "",
       pageNum: 1,
       pageSize: 10,
-      type: "inside",
+      type: topType,
+      dictProjectBusinessIds: '', // 业务类型id
+      dictProjectScopeIds: '', // 项目范围id
+      dictProjectAppIds: '', // 客户端id
     });
-  }, []);
+  }, [topType]);
 
   // 点击顶部的切换视图 按钮
   const topTypeFu = useCallback(
-    (val: "outer" | "inner") => {
+    (val: "item" | "inside") => {
       if (topType === val) return;
       setTopType(val);
-      resetSelectFu();
     },
-    [resetSelectFu, topType]
+    [topType]
   );
 
+  // 当 topType 改变时,重置筛选条件
+  useEffect(() => {
+    resetSelectFu();
+  }, [topType, resetSelectFu]); 
+
   // 权限-------项目视图 和 内控文件视图
   const authArr = useSelector((state: RootState) => state.A4Role.A4RoleAll);
 
   useEffect(() => {
     // 没有项目文件视图权限,一定有内控文件视图权限
-    if (!authArr.includes("1010")) setTopType("inner");
+    if (!authArr.includes("1010")) setTopType("inside");
   }, [authArr]);
 
   // 从仓库获取表格数据
   const tableInfo = useSelector(
     (state: RootState) => state.A1Project.tableInfo
   );
+
+  /**
+   * 业务类型 相关逻辑
+   */
+  // 从仓库 获取 业务类型 数据
+  const projectBusinessArr = useSelector(
+    (state: RootState) => state.A2Dict.A2Tab1_1Obj.projectBusiness
+  );
+  const projectBusinessArrForCheckbox = projectBusinessArr.map((v) => {
+    return {
+      label: v.name,
+      value: v.id,
+    }
+  });
+  // 选中的选项
+  const [checkedProjectBusinessArr, setCheckedProjectBusinessArr] = useState<CheckboxValueType[]>([]);
+  // 是否已全选
+  const isProjectBusinessAllChecked = projectBusinessArr.length === checkedProjectBusinessArr.length;
+  // 是否选了一部分
+  const isProjectBusinessPartiallyChecked = checkedProjectBusinessArr.length > 0 && checkedProjectBusinessArr.length < projectBusinessArr.length;
+  // 全选项 选中状态变化
+  const onProjectBusinessCheckAllChange: CheckboxProps['onChange'] = (e) => {
+    setCheckedProjectBusinessArr(e.target.checked ? projectBusinessArr.map((i) => {return i.id}) : []);
+  };
+  // 普通选项 选中状态变化
+  const onProjectBusinessNormalCheckboxChange = (list: CheckboxValueType[]) => {
+    setCheckedProjectBusinessArr(list);
+  };
+  useEffect(() => {
+    setFromData(currentFromData => ({
+      ...currentFromData,
+      dictProjectBusinessIds: checkedProjectBusinessArr.join(','),
+      pageNum: 1
+    }));
+  }, [checkedProjectBusinessArr]);
+  /**
+   * end of 业务类型 相关逻辑
+   */
+
+  /**
+   * 项目范围 相关逻辑
+   */
+  // 从仓库 获取 项目范围 数据
+  const projectScopeArr = useSelector(
+    (state: RootState) => state.A2Dict.A2Tab1_1Obj.projectScope
+  );
+  const projectScopeArrForCheckbox = projectScopeArr.map((v) => {
+    return {
+      label: v.name,
+      value: v.id,
+    }
+  });
+  // 选中的选项
+  const [checkedProjectScopeArr, setCheckedProjectScopeArr] = useState<CheckboxValueType[]>([]);
+  // 是否已全选
+  const isProjectScopeAllChecked = projectScopeArrForCheckbox.length === checkedProjectScopeArr.length;
+  // 是否选了一部分
+  const isProjectScopePartiallyChecked = checkedProjectScopeArr.length > 0 && checkedProjectScopeArr.length < projectScopeArr.length;
+  // 全选项 选中状态变化
+  const onProjectScopeCheckAllChange: CheckboxProps['onChange'] = (e) => {
+    setCheckedProjectScopeArr(e.target.checked ? projectScopeArrForCheckbox.map((i) => {return i.value}) : []);
+  };
+  // 普通选项 选中状态变化
+  const onProjectScopeNormalCheckboxChange = (list: CheckboxValueType[]) => {
+    setCheckedProjectScopeArr(list);
+  };
+  useEffect(() => {
+    setFromData(currentFromData => ({
+      ...currentFromData,
+      dictProjectScopeIds: checkedProjectScopeArr.join(','),
+      pageNum: 1
+    }));
+  }, [checkedProjectScopeArr]);
+  
+  /**
+   * end of 项目范围 相关逻辑
+   */
+
+  /**
+   * 客户端 相关逻辑
+   */
+  // 从仓库 获取 客户端 数据
+  const projectAppArr = useSelector(
+    (state: RootState) => state.A2Dict.A2Tab1_1Obj.projectApp
+  );
+  const projectAppArrForCheckbox = projectAppArr.map((v) => {
+    return {
+      label: v.name,
+      value: v.id,
+    }
+  });
+  // 选中的选项
+  const [checkedProjectAppArr, setCheckedProjectAppArr] = useState<CheckboxValueType[]>([]);
+  // 是否已全选
+  const isProjectAppAllChecked = projectAppArrForCheckbox.length === checkedProjectAppArr.length;
+  // 是否选了一部分
+  const isProjectAppPartiallyChecked = checkedProjectAppArr.length > 0 && checkedProjectAppArr.length < projectAppArr.length;
+  // 全选项 选中状态变化
+  const onProjectAppCheckAllChange: CheckboxProps['onChange'] = (e) => {
+    setCheckedProjectAppArr(e.target.checked ? projectAppArrForCheckbox.map((i) => {return i.value}) : []);
+  };
+  // 普通选项 选中状态变化
+  const onProjectAppNormalCheckboxChange = (list: CheckboxValueType[]) => {
+    setCheckedProjectAppArr(list);
+  };
+  useEffect(() => {
+    setFromData(currentFromData => ({
+      ...currentFromData,
+      dictProjectAppIds: checkedProjectAppArr.join(','),
+      pageNum: 1
+    }));
+  }, [checkedProjectAppArr]);
+  /**
+   * end of 客户端 相关逻辑
+   */
+  
+  /**
+   * 项目类型筛选按钮显示的文字
+   */
+  const [projectTypeCurrentText, setProjectTypeCurrentText] = useState('全部类型')
+  useEffect(() => {
+    setProjectTypeCurrentText(v => {
+      if (
+        (checkedProjectBusinessArr.length === 0 && checkedProjectScopeArr.length === 0 && checkedProjectAppArr.length === 0) ||
+        (checkedProjectBusinessArr.length === projectBusinessArr.length && checkedProjectScopeArr.length === projectScopeArr.length && checkedProjectAppArr.length === projectAppArr.length)
+      ) {
+        return '全部类型'
+      } else {
+        // 把checkedProjectBusinessArr各元素对应的name存入一个数组
+        const checkedProjectBusinessArrName = checkedProjectBusinessArr.map(v => {
+          const item = projectBusinessArr.find(i => i.id === v)
+          return item ? item.name : ''
+        })
+        // 把checkedProjectScopeArr各元素对应的name存入一个数组
+        const checkedProjectScopeArrName = checkedProjectScopeArr.map(v => {
+          const item = projectScopeArr.find(i => i.id === v)
+          return item ? item.name : ''
+        })
+        // 把checkedProjectAppArr各元素对应的name存入一个数组
+        const checkedProjectAppArrName = checkedProjectAppArr.map(v => {
+          const item = projectAppArr.find(i => i.id === v)
+          return item ? item.name : ''
+        })
+        // 把3个数组合并成一个数组,再转成字符串
+        return checkedProjectBusinessArrName.concat(checkedProjectScopeArrName, checkedProjectAppArrName).join(',')
+      }
+    })
+  }, [
+    checkedProjectBusinessArr,
+    checkedProjectScopeArr,
+    checkedProjectAppArr,
+    projectBusinessArr,
+    projectScopeArr,
+    projectAppArr
+  ])
+  
+  
   // 页码变化
   const paginationChange = useCallback(
     () => (pageNum: number, pageSize: number) => {
@@ -206,7 +381,7 @@ function A1Project() {
       },
     ];
 
-    if (topType === "inner") {
+    if (topType === "inside") {
       arr.push(
         {
           title: "收集文件类型",
@@ -259,7 +434,7 @@ function A1Project() {
           >
             查看
           </Button>
-          {topType === "inner" ? (
+          {topType === "inside" ? (
             <>
               <AuthCom aId="1104">
                 <Button
@@ -347,8 +522,8 @@ function A1Project() {
           <div className="A1SearchRow" hidden={isOneTopType}>
             <AuthCom aId="1010">
               <Button
-                onClick={() => topTypeFu("outer")}
-                type={topType === "outer" ? "primary" : "default"}
+                onClick={() => topTypeFu("item")}
+                type={topType === "item" ? "primary" : "default"}
               >
                 项目文件视图
               </Button>
@@ -356,8 +531,8 @@ function A1Project() {
 
             <AuthCom aId="1020">
               <Button
-                onClick={() => topTypeFu("inner")}
-                type={topType === "inner" ? "primary" : "default"}
+                onClick={() => topTypeFu("inside")}
+                type={topType === "inside" ? "primary" : "default"}
               >
                 内控文件视图
               </Button>
@@ -416,8 +591,77 @@ function A1Project() {
             />
           </div>
 
+          {topType === "item" ? (
+            <div className="A1SearchRow">
+              <span>项目类型:</span>
+              <Popover
+                trigger="click"
+                placement="bottomLeft"
+                content={
+                  <div className={styles.projectTypePopOver}>
+                    <div className={styles.row}>
+                      <span className={styles.title} title="甲方的业务类型">业务类型:</span>
+                      <Checkbox
+                        indeterminate={isProjectBusinessPartiallyChecked}
+                        onChange={onProjectBusinessCheckAllChange}
+                        checked={isProjectBusinessAllChecked}
+                        className={styles.selectAll}
+                      >
+                        全选
+                      </Checkbox>
+                      <Checkbox.Group
+                        options={projectBusinessArrForCheckbox}
+                        value={checkedProjectBusinessArr}
+                        onChange={onProjectBusinessNormalCheckboxChange}
+                      />
+                    </div>
+
+                    <div className={styles.row}>
+                      <span className={styles.title} title="项目的建设范围">项目范围:</span>
+                      <Checkbox
+                        indeterminate={isProjectScopePartiallyChecked}
+                        onChange={onProjectScopeCheckAllChange}
+                        checked={isProjectScopeAllChecked}
+                        className={styles.selectAll}
+                      >
+                        全选
+                      </Checkbox>
+                      <Checkbox.Group
+                        options={projectScopeArrForCheckbox}
+                        value={checkedProjectScopeArr}
+                        onChange={onProjectScopeNormalCheckboxChange}
+                      />
+                    </div>
+
+                    <div className={styles.row}>
+                      <span className={styles.title} title="项目成果的呈现载体">客户端:</span>
+                      <Checkbox
+                        indeterminate={isProjectAppPartiallyChecked}
+                        onChange={onProjectAppCheckAllChange}
+                        checked={isProjectAppAllChecked}
+                        className={styles.selectAll}
+                      >
+                        全选
+                      </Checkbox>
+                      <Checkbox.Group
+                        options={projectAppArrForCheckbox}
+                        value={checkedProjectAppArr}
+                        onChange={onProjectAppNormalCheckboxChange}
+                      />
+                    </div>
+                  </div>
+                }
+              >
+                <Button className="projectTypeFilterButton">
+                  {projectTypeCurrentText}
+                  <DownOutlined />
+                </Button>
+              </Popover>
+            </div>
+          ) : null}
+          
           {/* 通过顶部状态 动态渲染 这3个下拉框 */}
-          {topType === "outer" ? null : (
+          {topType === "item" ? null : (
             <>
               <div className="A1SearchRow">
                 <span>是否存在待审批文件:</span>

+ 10 - 1
src/pages/A2Dict/A2Tab1/A2Tab1Add/index.tsx

@@ -46,7 +46,16 @@ function A2Tab1Add({ info, closeFu, addFu }: Props) {
   }, [info]);
 
   const A2Tilele = useMemo(() => {
-    let txt1 = info.type === "job" ? "职能" : "状态";
+    const type2text = {
+      job: "职能",
+      status: "状态",
+      main: '主体',
+      dept: '部门',
+      projectBusiness: '业务类型',
+      projectScope: '项目范围',
+      projectApp: '客户端',
+    }
+    let txt1 = type2text[info.type];
     let txt2 = info.id === -1 ? "新增" : "编辑";
     return txt2 + txt1;
   }, [info.id, info.type]);

+ 1 - 1
src/pages/A2Dict/A2Tab1/A2Table1.tsx

@@ -10,7 +10,7 @@ import { MessageFu } from "@/utils/message";
 type Props = {
   editFu: (item: A2Tab1_1) => void;
   upTaleFu: () => void;
-  type: "job" | "status";
+  type: "job" | "status" | "main" | "dept" | "projectBusiness" | "projectScope" | "projectApp";
 };
 
 function A2Table1({ editFu, upTaleFu, type }: Props) {

+ 35 - 1
src/pages/A2Dict/A2Tab1/index.tsx

@@ -69,6 +69,40 @@ function A2Tab1() {
 
       <div className="A2tableBox">
         <div className="A2tableBoxBtn">
+          <h3>签订主体</h3>
+          <Button
+            type="primary"
+            onClick={() => setAddInfo1({ id: -1, type: "main" } as A2Tab1_1)}
+          >
+            新增
+          </Button>
+        </div>
+        <A2Table1
+          editFu={(item) => setAddInfo1(item)}
+          upTaleFu={() => getListFu1()}
+          type="main"
+        />
+      </div>
+
+      <div className="A2tableBox">
+        <div className="A2tableBoxBtn">
+          <h3>业务部门</h3>
+          <Button
+            type="primary"
+            onClick={() => setAddInfo1({ id: -1, type: "dept" } as A2Tab1_1)}
+          >
+            新增
+          </Button>
+        </div>
+        <A2Table1
+          editFu={(item) => setAddInfo1(item)}
+          upTaleFu={() => getListFu1()}
+          type="dept"
+        />
+      </div>
+      
+      <div className="A2tableBox">
+        <div className="A2tableBoxBtn">
           <h3>阶段</h3>
           <Button
             type="primary"
@@ -86,7 +120,7 @@ function A2Tab1() {
         />
       </div>
 
-      {/* 新增职能 和 状态 */}
+      {/* 新增职能、状态、签订主体、业务部门 */}
       {addInfo1.id ? (
         <A2Tab1Add
           info={addInfo1}

+ 33 - 0
src/pages/A2Dict/A2Tab3/index.module.scss

@@ -0,0 +1,33 @@
+.A2Tab1 {
+  :global {
+
+    .A2tableBox {
+      border-top: 1px solid var(--themeColor);
+      padding-top: 15px;
+      margin-bottom: 20px;
+
+      .A2tableBoxBtn {
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        margin-bottom: 10px;
+        padding: 0 10px;
+
+        &>h3 {
+          font-size: 18px;
+          font-weight: 700;
+        }
+      }
+
+      #A2Table3 {
+        .ant-table-body {
+          overflow-y: auto !important;
+
+
+        }
+      }
+    }
+
+
+  }
+}

+ 91 - 0
src/pages/A2Dict/A2Tab3/index.tsx

@@ -0,0 +1,91 @@
+import React, { useCallback, useEffect, useState } from "react";
+import styles from "./index.module.scss";
+import { useDispatch } from "react-redux";
+import { A2_APIgetList1 } from "@/store/action/A2Dict";
+import A2Table1 from "../A2Tab1/A2Table1";
+import { Button } from "antd";
+import { A2Tab1_1 } from "@/types/api/A2Dict";
+import A2Tab1Add from "../A2Tab1/A2Tab1Add";
+
+function A2Tab3() {
+  const dispatch = useDispatch();
+
+  const getListFu1 = useCallback(() => {
+    dispatch(A2_APIgetList1());
+  }, [dispatch]);
+
+  useEffect(() => {
+    getListFu1();
+  }, [getListFu1]);
+
+  const [addInfo1, setAddInfo1] = useState({} as A2Tab1_1);
+
+  return (
+    <div className={styles.A2Tab1}>
+      <div className="A2tableBox">
+        <div className="A2tableBoxBtn">
+          <h3>业务类型</h3>
+          <Button
+            type="primary"
+            onClick={() => setAddInfo1({ id: -1, type: "projectBusiness" } as A2Tab1_1)}
+          >
+            新增
+          </Button>
+        </div>
+        <A2Table1
+          editFu={(item) => setAddInfo1(item)}
+          upTaleFu={() => getListFu1()}
+          type="projectBusiness"
+        />
+      </div>
+
+      <div className="A2tableBox">
+        <div className="A2tableBoxBtn">
+          <h3>项目范围</h3>
+          <Button
+            type="primary"
+            onClick={() => setAddInfo1({ id: -1, type: "projectScope" } as A2Tab1_1)}
+          >
+            新增
+          </Button>
+        </div>
+        <A2Table1
+          editFu={(item) => setAddInfo1(item)}
+          upTaleFu={() => getListFu1()}
+          type="projectScope"
+        />
+      </div>
+
+      <div className="A2tableBox">
+        <div className="A2tableBoxBtn">
+          <h3>客户端</h3>
+          <Button
+            type="primary"
+            onClick={() => setAddInfo1({ id: -1, type: "projectApp" } as A2Tab1_1)}
+          >
+            新增
+          </Button>
+        </div>
+        <A2Table1
+          editFu={(item) => setAddInfo1(item)}
+          upTaleFu={() => getListFu1()}
+          type="projectApp"
+        />
+      </div>
+
+      {/* 新增功能弹窗 */}
+      {addInfo1.id ? (
+        <A2Tab1Add
+          info={addInfo1}
+          closeFu={() => setAddInfo1({} as A2Tab1_1)}
+          addFu={() => getListFu1()}
+        />
+      ) : null}
+
+    </div>
+  );
+}
+
+const MemoA2Tab3 = React.memo(A2Tab3);
+
+export default MemoA2Tab3;

+ 8 - 1
src/pages/A2Dict/index.module.scss

@@ -2,7 +2,14 @@
   padding: 20px 15px;
   background-color: #fff;
   border-radius: 10px;
-
+  >.A2Top {
+    >.tabBtn {
+      margin-right: 10px;
+      &:last-child{
+        margin-right: 0;
+      }
+    }
+  }
   :global {
     .A2Main {
       width: 100%;

+ 6 - 3
src/pages/A2Dict/index.tsx

@@ -3,9 +3,11 @@ import styles from "./index.module.scss";
 import { Button } from "antd";
 import A2Tab1 from "./A2Tab1";
 import A2Tab2 from "./A2Tab2";
+import A2Tab3 from "./A2Tab3";
 
 const topBtnArr = [
   { id: 1, name: "项目属性" },
+  { id: 3, name: "项目类型" },
   { id: 2, name: "内控文件属性" },
 ];
 
@@ -15,9 +17,10 @@ function A2Dict() {
   return (
     <div className={styles.A2Dict}>
       <div className="pageTitle">字典管理</div>
-      <div className="A2Top">
+      <div className={styles.A2Top}>
         {topBtnArr.map((v) => (
           <Button
+            className={styles.tabBtn}
             onClick={() => setAcId(v.id)}
             key={v.id}
             type={v.id === acId ? "primary" : "default"}
@@ -25,14 +28,14 @@ function A2Dict() {
             {v.name}
           </Button>
         ))}
-        {acId === 1 ? (
+        {acId === 1 || acId === 3 ? (
           <>
             &emsp;&emsp;注:删除字典名称时,与该字典名称关联的数据都将关联到“其它”
           </>
         ) : null}
       </div>
 
-      <div className="A2Main">{acId === 1 ? <A2Tab1 /> : <A2Tab2 />}</div>
+      <div className="A2Main">{acId === 1 ? <A2Tab1 /> : acId === 2 ? <A2Tab2 /> : <A2Tab3 />}</div>
     </div>
   );
 }

+ 16 - 1
src/store/action/A2Dict.ts

@@ -3,7 +3,7 @@ import { AppDispatch } from "..";
 import { A2Tab1Type, A2Tab1_1 } from "@/types/api/A2Dict";
 import { A5TableType } from "@/types";
 /**
- * 获取项目属性 职能,状态
+ * 获取项目属性:职能,状态,签订主体,业务部门,业务类型,项目范围,客户端
  */
 export const A2_APIgetList1 = () => {
   return async (dispatch: AppDispatch) => {
@@ -14,15 +14,30 @@ export const A2_APIgetList1 = () => {
 
       const arr1: A2Tab1_1[] = [];
       const arr2: A2Tab1_1[] = [];
+      const arrMain: A2Tab1_1[] = []; // 签订主体
+      const arrDept: A2Tab1_1[] = []; // 业务部门
+      const arrProjectBusiness: A2Tab1_1[] = []; // 业务类型
+      const arrProjectScope: A2Tab1_1[] = []; // 项目范围
+      const arrProjectApp: A2Tab1_1[] = []; // 客户端
 
       arrTemp.forEach((v) => {
         if (v.type === "job") arr1.push(v);
         else if (v.type === "status") arr2.push(v);
+        else if (v.type === "main") arrMain.push(v);
+        else if (v.type === "dept") arrDept.push(v);
+        else if (v.type === "projectBusiness") arrProjectBusiness.push(v);
+        else if (v.type === "projectScope") arrProjectScope.push(v);
+        else if (v.type === "projectApp") arrProjectApp.push(v);
       });
 
       const obj: A2Tab1Type = {
         job: arr1,
         status: arr2,
+        main: arrMain,
+        dept: arrDept,
+        projectBusiness: arrProjectBusiness,
+        projectScope: arrProjectScope,
+        projectApp: arrProjectApp,
       };
       dispatch({ type: "A2/getInfo1_1", payload: obj });
     }

+ 5 - 0
src/store/reducer/A2Dict.ts

@@ -7,6 +7,11 @@ const initState = {
   A2Tab1_1Obj: {
     status: [],
     job: [],
+    main: [],
+    dept: [],
+    projectBusiness: [],
+    projectScope: [],
+    projectApp: [],
   } as A2Tab1Type,
 
   // 阶段

+ 18 - 2
src/types/api/A1Project.d.ts

@@ -17,8 +17,24 @@ export type A1TableType = {
   unit: string;
   updateTime: string;
   amount: string;
-  statusName:string
-  hasAuditStatus:0|1
+  statusName:string;
+  hasAuditStatus:0|1;
+  // 签订主体
+  dictMainId?: number; 
+  dictMainName?: string; 
+  // 业务部门
+  dictDeptId?: number;
+  dictDeptName?: string;
+  // 是否投标
+  isBid?: 0|1;
+  // 中标日期
+  bidDate?: string;
+  // 业务类型
+  dictProjectBusinessIds?: string;
+  // 项目范围
+  dictProjectScopeIds?: string;
+  // 客户端
+  dictProjectAppIds?: string;
 };
 
 export type A1OFileType = {

+ 6 - 1
src/types/api/A2Dict.d.ts

@@ -8,13 +8,18 @@ export type A2Tab1_1 = {
   name: string;
   parentId: number;
   sort: number;
-  type: "status" | "job";
+  type: "status" | "job" | "main" | "dept" | "projectBusiness" | "projectScope" | "projectApp";
   updateTime: string;
 };
 
 export type A2Tab1Type = {
   status: A2Tab1_1[];
   job: A2Tab1_1[];
+  main: A2Tab1_1[];
+  dept: A2Tab1_1[];
+  projectBusiness: A2Tab1_1[];
+  projectScope: A2Tab1_1[];
+  projectApp: A2Tab1_1[];
 };
 
 export type A2Tab2Type = {

+ 1 - 1
src/utils/http.ts

@@ -8,7 +8,7 @@ import { domShowFu } from "./domShow";
 export const baseURL =
   // 线下的图片地址需要加上/api/
   // process.env.NODE_ENV === "development"
-  //   ? "http://192.168.20.61:8054/api/"
+  //   ? "http://192.168.20.61:8054"
   //   : "";
   process.env.NODE_ENV === "development"
     ? "https://sit-projectfile.4dage.com"

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 11161 - 0
yarn.lock