chenlei hace 5 meses
padre
commit
2e9d6125a9
Se han modificado 38 ficheros con 10445 adiciones y 7577 borrados
  1. 1 1
      package.json
  2. 8624 7107
      pnpm-lock.yaml
  3. 10 0
      src/App.scss
  4. 5 2
      src/api/assessment.ts
  5. 75 6
      src/api/management.ts
  6. 1 1
      src/components/AddIndexModal/index.tsx
  7. 37 14
      src/components/FileTemplateModal/index.tsx
  8. 5 2
      src/components/FileTemplateTable/index.tsx
  9. 6 0
      src/constants.ts
  10. 3 2
      src/pages/Assessment/Index/CreateOrEdit/index.tsx
  11. 112 34
      src/pages/Assessment/Index/components/Container/index.tsx
  12. 19 2
      src/pages/Assessment/Index/components/Sidebar/index.tsx
  13. 1 1
      src/pages/Assessment/Index/index.tsx
  14. 64 0
      src/pages/AssessmentDetail/components/EvaluationFormModal/index.tsx
  15. 249 130
      src/pages/AssessmentDetail/components/IndexAssessment/index.tsx
  16. 15 0
      src/pages/AssessmentDetail/components/IndexDetailModal/index.module.scss
  17. 236 0
      src/pages/AssessmentDetail/components/IndexDetailModal/index.tsx
  18. 11 0
      src/pages/AssessmentDetail/components/OverallAssessment/index.module.scss
  19. 133 12
      src/pages/AssessmentDetail/components/OverallAssessment/index.tsx
  20. 80 0
      src/pages/AssessmentDetail/components/SelfReportScoreModal/index.tsx
  21. 2 2
      src/pages/AssessmentDetail/components/EvaluationModal/index.tsx
  22. 89 42
      src/pages/AssessmentDetail/index.tsx
  23. 11 10
      src/pages/Management/Evaluation/index.tsx
  24. 58 17
      src/pages/Management/Files/index.tsx
  25. 7 5
      src/pages/Management/Form/index.tsx
  26. 110 78
      src/pages/Management/Index/SettingIndex/index.tsx
  27. 7 2
      src/pages/Management/Index/SettingRole/index.tsx
  28. 13 0
      src/pages/Management/Index/components/AllocationOfIndexDataModal/index.module.scss
  29. 167 0
      src/pages/Management/Index/components/AllocationOfIndexDataModal/index.tsx
  30. 124 72
      src/pages/Management/Index/components/AllocationOfIndexModal/index.tsx
  31. 46 0
      src/pages/Management/Index/components/WarningModal/index.tsx
  32. 1 0
      src/pages/Management/Index/components/index.ts
  33. 37 14
      src/pages/Management/Index/index.tsx
  34. 1 1
      src/router/index.tsx
  35. 5 7
      src/types/assessment.ts
  36. 20 1
      src/types/index.ts
  37. 47 12
      src/types/management.ts
  38. 13 0
      src/utils/index.ts

+ 1 - 1
package.json

@@ -6,7 +6,7 @@
     "@ant-design/icons": "^5.1.4",
     "@ant-design/pro-components": "^2.8.1",
     "@babel/core": "^7.16.0",
-    "@dage/pc-components": "^1.3.5",
+    "@dage/pc-components": "^1.3.11",
     "@dage/service": "^1.0.5",
     "@dage/utils": "^1.0.2",
     "@pmmmwh/react-refresh-webpack-plugin": "^0.5.3",

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 8624 - 7107
pnpm-lock.yaml


+ 10 - 0
src/App.scss

@@ -191,3 +191,13 @@ body {
   color: rgba(36, 36, 36, 0.5);
   font-size: 12px;
 }
+
+.ant-tree
+  .ant-tree-treenode.ant-tree-treenode-disabled.ant-tree-treenode-selected
+  .ant-tree-node-content-wrapper {
+  background: none;
+}
+
+.no-list .ant-upload-list {
+  display: none;
+}

+ 5 - 2
src/api/assessment.ts

@@ -31,9 +31,12 @@ export const createAssIndexApi = (params: any) => {
   return requestByPost("/api/cms/norm/save", params);
 };
 
-export const saveAssEntityApi = (params: IFileTemplateFormParams) => {
+export const saveEntityApi = (
+  params: IFileTemplateFormParams,
+  type: "norm" | "assess"
+) => {
   return requestByPost<IFileTemplateFormResponse>(
-    "/api/cms/norm/file/saveEntity",
+    `/api/cms/${type}/file/saveEntity`,
     params
   );
 };

+ 75 - 6
src/api/management.ts

@@ -12,6 +12,11 @@ import {
   IManageDeptAllocationOfIndexItem,
   IManageRoleGroupItem,
   IManageAssOperationResponse,
+  IAssIndexDetail,
+  ISaveManageFileParams,
+  YES_OR_NO,
+  IManageNormItem,
+  MaterialType,
 } from "@/types";
 import { requestByGet, requestByPost, requestPagination } from "@dage/service";
 
@@ -82,18 +87,28 @@ export const setManageAssFixedApi = (
   id: string | number,
   indexIds: string[]
 ) => {
-  return requestByPost<IManageAssessmentIndex[]>(
-    `/api/cms/assessFixed/save/${id}`,
-    indexIds
-  );
+  return requestByPost(`/api/cms/assessFixed/save/${id}`, indexIds);
+};
+
+export const setManageAssOperationApi = (
+  id: string | number,
+  indexIds: string[]
+) => {
+  return requestByPost(`/api/cms/assessOperation/save/${id}`, indexIds);
 };
 
 export const getManageFormListApi = (params: IManageFormListParams) => {
   return requestPagination<IManageFormItem>("/api/cms/fill/pageList", params);
 };
 
-export const getManageFormDetailApi = (id: number | string) => {
-  return requestByGet<IManageFormDetail>(`/api/cms/fill/detail/${id}`);
+// 部门考核单概况
+export const getManageFormDetailApi = (
+  id: number | string,
+  accessId: number | string
+) => {
+  return requestByGet<IManageFormDetail>(
+    `/api/cms/fill/detail/${id}/${accessId}`
+  );
 };
 
 export const getManageEvaluationListApi = (params: IManageFormListParams) => {
@@ -143,3 +158,57 @@ export const saveManageRoleGroupApi = (params: {
 }) => {
   return requestByPost<IManageRoleGroupItem>("/api/cms/group/save", params);
 };
+
+export const saveManageAssOperationWeightApi = (
+  id: number | string,
+  weight: number | string
+) => {
+  return requestByPost(`/api/cms/assessOperation/setWeight/${id}/${weight}`);
+};
+
+// 分配指标-检查指标被分配给多个部门
+export const checkManageIndexApi = (params: {
+  assessId: number | string;
+  normIds: string;
+}) => {
+  return requestByPost<IAssIndexDetail[]>("/api/cms/dept/norm/check", params);
+};
+
+// 考核填报-上传附件
+export const saveManageFileApi = (params: ISaveManageFileParams) => {
+  return requestByPost("/api/cms/fill/file/saveEntity", params);
+};
+
+// 指标考核-列表
+export const getManageNormListApi = (params: {
+  deptId: string | number;
+  searchKey?: string;
+  uploadStatus?: YES_OR_NO;
+}) => {
+  return requestByPost<IManageNormItem[]>("/api/cms/fill/norm/getList", params);
+};
+
+// 附件管理-列表
+export const getManageFileListAPi = (params: any) => {
+  return requestByPost("/api/cms/assessFile/pageList", params);
+};
+
+// 自评得分
+export const saveSelfScoreApi = (
+  id: number | string,
+  score: number | string
+) => {
+  return requestByGet(`/api/cms/fill/norm/updateScore/${id}/${score}`);
+};
+
+// 查看指标&考核单资料列表
+export const getFileListApi = (moduleId: number, type: "norm" | "access") => {
+  return requestByGet<MaterialType[]>(
+    `/api/cms/fill/norm/getFile/${moduleId}/${type}`
+  );
+};
+
+// 删除资料
+export const deleteFileApi = (ids: string | number) => {
+  return requestByGet(`/api/cms/fill/file/removes/${ids}`);
+};

+ 1 - 1
src/components/AddIndexModal/index.tsx

@@ -45,7 +45,7 @@ export const AddIndexModal: FC<AddIndexModalProps> = ({
   };
 
   const handleSubmit = async (values: any) => {
-    onOk?.(values.checkedKeys.map((i: number) => String(i)));
+    onOk?.(values.onlyChildKeys.map((i: number) => String(i)));
     // onOk?.(values.checkedKeys, getSelectedNodes(values.checkedKeys, treeData));
     handleCancel();
   };

+ 37 - 14
src/components/FileTemplateModal/index.tsx

@@ -7,7 +7,7 @@ import {
   ModalProps,
   Radio,
 } from "antd";
-import { FC, useEffect, useMemo } from "react";
+import { FC, useEffect, useMemo, useState } from "react";
 import style from "./index.module.scss";
 import {
   DageUpload,
@@ -17,12 +17,20 @@ import {
 } from "@dage/pc-components";
 import { FILE_TYPE_ENUM } from "./constants";
 import { IFileTemplateForm, IFileTemplateFormParams } from "@/types";
-import { saveAssEntityApi } from "@/api";
+import { saveEntityApi } from "@/api";
 import { getBaseURL } from "@dage/service";
 
 export interface FileTemplateModalProps extends ModalProps {
   item?: IFileTemplateFormParams | null;
-  module?: "norm" | "assess";
+  /**
+   * 层级
+   * @default 1
+   */
+  level?: number;
+  module: "norm" | "assess";
+  moduleId?: number;
+  // 如果level为2,则是上传附件,需要传所属资料id
+  parentId?: number;
   onCancel?: () => void;
   onOk?: (val: any, isEdit?: boolean) => void;
 }
@@ -30,7 +38,10 @@ export interface FileTemplateModalProps extends ModalProps {
 export const FileTemplateModal: FC<FileTemplateModalProps> = ({
   item,
   open,
+  level = 1,
   module,
+  moduleId,
+  parentId,
   onOk,
   onCancel,
   ...rest
@@ -38,6 +49,7 @@ export const FileTemplateModal: FC<FileTemplateModalProps> = ({
   const [form] = Form.useForm<IFileTemplateForm>();
   const typesVal = Form.useWatch("suffix", form);
   const baseUrl = getBaseURL();
+  const [loading, setLoading] = useState(false);
 
   const checkAll = useMemo(
     () => FILE_TYPE_ENUM.length === typesVal?.length,
@@ -62,17 +74,28 @@ export const FileTemplateModal: FC<FileTemplateModalProps> = ({
   const handleSubmit = async (values: IFileTemplateForm) => {
     const { file, suffix, ...rest } = values;
 
-    const data = await saveAssEntityApi({
-      ...rest,
-      level: 1,
-      module: module as "norm" | "assess",
-      fileName: file?.[0].response?.fileName,
-      filePath: file?.[0].response?.filePath,
-      suffix: suffix?.join(","),
-    });
+    try {
+      setLoading(true);
+      const data = await saveEntityApi(
+        {
+          ...rest,
+          level,
+          module,
+          moduleId,
+          parentId,
+          fileName: file?.[0].response?.fileName,
+          filePath: file?.[0].response?.filePath,
+          suffix: suffix?.join(","),
+          id: item?.id,
+        },
+        module
+      );
 
-    onOk?.(data, Boolean(item));
-    handleCancel();
+      onOk?.(data, Boolean(item));
+      handleCancel();
+    } finally {
+      setLoading(false);
+    }
   };
 
   const onCheckAllChange: CheckboxProps["onChange"] = (e) => {
@@ -115,7 +138,7 @@ export const FileTemplateModal: FC<FileTemplateModalProps> = ({
             open={open}
             width={640}
             okButtonProps={{
-              disabled: consumer?.uploading,
+              disabled: consumer?.uploading || loading,
             }}
             onOk={handleConfirm}
             onCancel={handleCancel}

+ 5 - 2
src/components/FileTemplateTable/index.tsx

@@ -12,7 +12,8 @@ export interface FileTemplateTableProps {
    * norm:指标 | assess:考核
    * @default 'assess'
    */
-  module?: "norm" | "assess";
+  module: "norm" | "assess";
+  moduleId?: number;
   value?: IFileTemplateFormResponse[];
   tips?: string;
   onChange?: (val: IFileTemplateFormResponse[]) => void;
@@ -21,7 +22,8 @@ export interface FileTemplateTableProps {
 export const FileTemplateTable: FC<FileTemplateTableProps> = ({
   tips,
   value,
-  module = "assess",
+  module,
+  moduleId,
   onChange,
 }) => {
   const [checkedItem, setCheckedItem] =
@@ -117,6 +119,7 @@ export const FileTemplateTable: FC<FileTemplateTableProps> = ({
         item={checkedItem}
         open={templateVisible}
         module={module}
+        moduleId={moduleId}
         onOk={handleTemplate}
         onCancel={handleCloseModal}
       />

+ 6 - 0
src/constants.ts

@@ -19,6 +19,9 @@ export const PUBLISH_STATUS_MAP = {
   },
 };
 
+/**
+ * 考核单状态
+ */
 export const PUBLISH_STATUS_OPTIONS = [
   {
     label: "待发布",
@@ -70,6 +73,9 @@ export const ASSESSMENT_TYPE_OPTIONS = [
   },
 ];
 
+/**
+ * 填报状态
+ */
 export const DEPT_STATUS_MAP = {
   [DEPT_STATUS_ENUM.PENDING_SUBMIT]: {
     label: "待填报",

+ 3 - 2
src/pages/Assessment/Index/CreateOrEdit/index.tsx

@@ -143,9 +143,10 @@ const CreateOrEditIndex: FC = () => {
 
     await saveAssIndexApi({
       ...rest,
-      level: 1,
+      level: (parentNode?.level || 0) + 1,
       type: params.type,
       id: detail?.id,
+      isPoint: rest.isPoint || 0,
       gistIds: Array.isArray(data) ? data.join(",") : undefined,
     });
     navigate(-1);
@@ -481,7 +482,7 @@ const CreateOrEditIndex: FC = () => {
             )}
 
             <Form.Item label="需上传资料" name="materialIds">
-              <FileTemplateTable />
+              <FileTemplateTable module="norm" />
             </Form.Item>
           </>
         )}

+ 112 - 34
src/pages/Assessment/Index/components/Container/index.tsx

@@ -3,14 +3,16 @@ import { Table } from "antd";
 import style from "../../index.module.scss";
 import { DageLoading } from "@dage/pc-components";
 import { getAssIndexDetailApi } from "@/api";
-import { IAssIndexDetail, YES_OR_NO } from "@/types";
+import { ASS_INDEX_TYPE, IAssIndexDetail, YES_OR_NO } from "@/types";
 import { CONNECT_WITH_SYMBOL_OPTIONS, SYMBOL_OPTIONS } from "../../constants";
 
 export interface ContainerProps {
+  type: ASS_INDEX_TYPE;
   currentId: number | null;
 }
 
-export const Container: FC<ContainerProps> = ({ currentId }) => {
+export const Container: FC<ContainerProps> = ({ currentId, type }) => {
+  const isFixed = type === ASS_INDEX_TYPE.FIXED;
   const [loading, setLoading] = useState(false);
   const [detail, setDetail] = useState<IAssIndexDetail | null>(null);
   // 打分点
@@ -73,10 +75,77 @@ export const Container: FC<ContainerProps> = ({ currentId }) => {
         <p className={style.tableItemLabel}>指标说明</p>
         <div className={style.tableItemInner}>{detail?.remark || "-"}</div>
       </div>
-      <div className={style.tableItem}>
-        <p className={style.tableItemLabel}>是否为打分点</p>
-        <div className={style.tableItemInner}>{!isPoint ? "否" : "是"}</div>
-      </div>
+      {!isFixed && detail?.level === 3 && (
+        <>
+          <div className={style.tableItem}>
+            <p className={style.tableItemLabel}>考察要点</p>
+            <div className={style.tableItemInner}>
+              <Table
+                className="cus-table"
+                rowKey="id"
+                dataSource={detail.gists}
+                pagination={false}
+                columns={[
+                  {
+                    title: "要点名称",
+                    dataIndex: "name",
+                    align: "center",
+                  },
+                  {
+                    title: "一级博物馆",
+                    dataIndex: "one",
+                    align: "center",
+                  },
+                  {
+                    title: "二级博物馆",
+                    dataIndex: "two",
+                    align: "center",
+                  },
+                  {
+                    title: "三级博物馆",
+                    dataIndex: "three",
+                    align: "center",
+                  },
+                ]}
+              />
+            </div>
+          </div>
+          <div className={style.tableItem}>
+            <p className={style.tableItemLabel}>指标说明</p>
+            <div className={style.tableItemInner}>
+              <Table
+                className="cus-table"
+                rowKey="id"
+                dataSource={[detail]}
+                pagination={false}
+                columns={[
+                  {
+                    title: "一级博物馆",
+                    dataIndex: "gistOne",
+                    align: "center",
+                  },
+                  {
+                    title: "二级博物馆",
+                    dataIndex: "gistTwo",
+                    align: "center",
+                  },
+                  {
+                    title: "三级博物馆",
+                    dataIndex: "gistThree",
+                    align: "center",
+                  },
+                ]}
+              />
+            </div>
+          </div>
+        </>
+      )}
+      {isFixed && (
+        <div className={style.tableItem}>
+          <p className={style.tableItemLabel}>是否为打分点</p>
+          <div className={style.tableItemInner}>{!isPoint ? "否" : "是"}</div>
+        </div>
+      )}
       {isPoint && point && (
         <>
           <div className={style.tableItem}>
@@ -103,35 +172,44 @@ export const Container: FC<ContainerProps> = ({ currentId }) => {
           <div className={style.tableItemInner}>{warnStr}</div>
         )}
       </div>
-      <div className={style.tableItem}>
-        <p className={style.tableItemLabel}>需上传资料</p>
-        <div className={style.tableItemInner}>
-          <Table
-            className="cus-table"
-            rowKey="id"
-            dataSource={detail?.materials}
-            columns={[
-              {
-                title: "资料名称",
-                dataIndex: "name",
-                key: "name",
-                align: "center",
-              },
-              {
-                title: "必须上传",
-                align: "center",
-                render: (e) => (e.isUpload === YES_OR_NO.YES ? "是" : "否"),
-              },
-              {
-                title: "模板",
-                dataIndex: "fileName",
-                key: "fileName",
-                align: "center",
-              },
-            ]}
-          />
+      {(isFixed || detail?.level === 3) && (
+        <div className={style.tableItem}>
+          <p className={style.tableItemLabel}>需上传资料</p>
+          <div className={style.tableItemInner}>
+            <Table
+              className="cus-table"
+              rowKey="id"
+              pagination={false}
+              dataSource={detail?.materials}
+              columns={[
+                {
+                  title: "资料名称",
+                  dataIndex: "name",
+                  key: "name",
+                  align: "center",
+                },
+                {
+                  title: "必须上传",
+                  align: "center",
+                  render: (e) => (e.isUpload === YES_OR_NO.YES ? "是" : "否"),
+                },
+                {
+                  title: "模板",
+                  dataIndex: "fileName",
+                  key: "fileName",
+                  align: "center",
+                },
+              ]}
+            />
+          </div>
         </div>
-      </div>
+      )}
+      {!isFixed && detail && detail.level < 3 && (
+        <div className={style.tableItem}>
+          <p className={style.tableItemLabel}>指标权重</p>
+          <div className={style.tableItemInner}>{detail?.weight}</div>
+        </div>
+      )}
       <div className={style.tableItem}>
         <p className={style.tableItemLabel}>排序值</p>
         <div className={style.tableItemInner}>{detail?.sort}</div>

+ 19 - 2
src/pages/Assessment/Index/components/Sidebar/index.tsx

@@ -8,7 +8,7 @@ import {
 } from "@dage/pc-components";
 import { PlusOutlined } from "@ant-design/icons";
 import { deleteAssIndexApi, getAssIndexTreeApi } from "@/api";
-import { AssIndexTreeItemType, ASS_INDEX_TYPE } from "@/types";
+import { AssIndexTreeItemType, ASS_INDEX_TYPE, YES_OR_NO } from "@/types";
 import style from "../../index.module.scss";
 import { isNumber } from "lodash";
 
@@ -33,18 +33,34 @@ export const Sidebar: FC<SidebarProps> = ({
   const [treeData, setTreeData] = useState<AssIndexTreeItemType[]>([]);
   const [checkedKeys, setCheckedKeys] = useState<number[]>([]);
   const lastCheckedItem = useRef<any>(null);
+  const isFixed = type === ASS_INDEX_TYPE.FIXED;
+
+  const transformTreeData = (data: AssIndexTreeItemType[]) => {
+    return data.map((item) => {
+      // @ts-ignore
+      item.hideAddBtn = item.isPoint === YES_OR_NO.YES;
+
+      if (item.children && item.children.length > 0) {
+        item.children = transformTreeData(item.children);
+      }
+
+      return item;
+    });
+  };
 
   const getAssIndexTree = async () => {
     try {
       setLoading(true);
       const data = await getAssIndexTreeApi(type);
       if (data.length) {
+        // 防止再次选择
         data[0].disabled = true;
         setCurrentId(data[0].id);
         setCheckedKeys([data[0].id]);
         lastCheckedItem.current = data[0];
+
+        setTreeData(transformTreeData(data));
       }
-      setTreeData(data);
     } finally {
       setLoading(false);
     }
@@ -86,6 +102,7 @@ export const Sidebar: FC<SidebarProps> = ({
       {currentId && (
         <DageTreeActions
           defaultExpandAll
+          maxLevel={isFixed ? 5 : 2}
           treeData={treeData}
           selectedKeys={checkedKeys}
           onAdd={checkable ? undefined : handleAddNode}

+ 1 - 1
src/pages/Assessment/Index/index.tsx

@@ -42,7 +42,7 @@ const IndexPage = () => {
           setCurrentId={setCurrentId}
         />
 
-        <Container currentId={currentId} />
+        <Container type={type} currentId={currentId} />
       </div>
     </PageContainer>
   );

+ 64 - 0
src/pages/AssessmentDetail/components/EvaluationFormModal/index.tsx

@@ -0,0 +1,64 @@
+import { FC } from "react";
+import { Form, Input, InputNumber, Modal, ModalProps } from "antd";
+import style from "@/components/AddIndexModal/index.module.scss";
+
+export interface EvaluationModalProps extends Omit<ModalProps, "onOk"> {
+  onCancel?: () => void;
+  onOk?: (val: any) => void;
+}
+
+const { TextArea } = Input;
+
+export const EvaluationModal: FC<EvaluationModalProps> = ({
+  open,
+  onOk,
+  onCancel,
+  ...rest
+}) => {
+  const [form] = Form.useForm<any>();
+
+  const handleCancel = () => {
+    form.resetFields();
+    onCancel?.();
+  };
+
+  const handleConfirm = () => {
+    form.submit();
+  };
+
+  const handleSubmit = async (values: any) => {
+    onOk?.(values);
+    handleCancel();
+  };
+
+  return (
+    <Modal
+      className={style.modal}
+      title="评定意见"
+      okText="提交"
+      cancelText="取消"
+      open={open}
+      width={640}
+      okButtonProps={{
+        disabled: false,
+      }}
+      onOk={handleConfirm}
+      onCancel={handleCancel}
+      {...rest}
+    >
+      <Form
+        labelCol={{ span: 4, offset: 2 }}
+        form={form}
+        onFinish={handleSubmit}
+      >
+        <Form.Item name="">
+          <TextArea
+            rows={6}
+            maxLength={2000}
+            placeholder="请输入内容,不超过2000字"
+          />
+        </Form.Item>
+      </Form>
+    </Modal>
+  );
+};

+ 249 - 130
src/pages/AssessmentDetail/components/IndexAssessment/index.tsx

@@ -1,36 +1,253 @@
-import { FC } from "react";
+import { FC, useEffect, useMemo, useState } from "react";
 import { Button, Form, Input, Select, Table } from "antd";
-import { useNavigate } from "react-router-dom";
+import { useNavigate, useParams } from "react-router-dom";
+import { getManageNormListApi } from "@/api";
+import { debounce } from "lodash";
+import {
+  IManageFormDetail,
+  IManageIndexDetail,
+  IManageNormItem,
+  YES_OR_NO,
+} from "@/types";
+import { SelfReportScoreModal } from "../SelfReportScoreModal";
+import { ColumnsType } from "antd/es/table";
+import { IndexDetailModal } from "../IndexDetailModal";
 
 export interface IndexAssessmentProps {
+  detail: IManageIndexDetail | IManageFormDetail | null;
   isReportDetail?: boolean;
   isEvalutionDetail?: boolean;
 }
 
+const DEFAULT_PARAMS = {
+  searchKey: "",
+  uploadStatus: undefined,
+};
+
+const CONFIRM_OPTIONS = [
+  {
+    label: "全部",
+    value: "",
+  },
+  {
+    label: "已完成",
+    value: YES_OR_NO.YES,
+  },
+  {
+    label: "未完成",
+    value: YES_OR_NO.NO,
+  },
+];
+
 export const IndexAssessment: FC<IndexAssessmentProps> = ({
+  detail,
   isReportDetail,
   isEvalutionDetail,
 }) => {
   const navigate = useNavigate();
+  const routeParams = useParams();
+  const [loading, setLoading] = useState(false);
+  const [params, setParams] = useState<any>({
+    ...DEFAULT_PARAMS,
+  });
+  const [list, setList] = useState<IManageNormItem[]>([]);
+  const [checkedItem, setCheckedItem] = useState<null | IManageNormItem>(null);
+  const [selfReportVisible, setSelfReportVisible] = useState(false);
+  const [indexDetailVisible, setIndexDetailVisible] = useState(false);
+  const columns = useMemo(() => {
+    const stack: ColumnsType<IManageNormItem> = [
+      {
+        title: "序号",
+        align: "center",
+        minWidth: 80,
+        render: (val, record, index) => index + 1,
+      },
+      {
+        title: "标题",
+        dataIndex: "name",
+        align: "center",
+        minWidth: 200,
+      },
+      {
+        title: "说明",
+        minWidth: 300,
+        align: "center",
+        render: (val) =>
+          val.remark ? (
+            <p className="limit-line line-2">{val.remark}</p>
+          ) : (
+            <p className="empty-text">(空)</p>
+          ),
+      },
+      {
+        title: "分值",
+        align: "center",
+        minWidth: 100,
+        render: (val) => val.score || <p className="empty-text">(空)</p>,
+      },
+      {
+        title: "资料上传 ",
+        minWidth: 120,
+        align: "center",
+        render: (val) => (
+          <Button
+            type="link"
+            onClick={() => {
+              setCheckedItem(val);
+              setIndexDetailVisible(true);
+            }}
+          >
+            查看
+          </Button>
+        ),
+      },
+      {
+        title: "上传状态",
+        align: "center",
+        minWidth: 120,
+        render: (val) => <p className="empty-text">(空)</p>,
+      },
+      {
+        title: "打分点",
+        align: "center",
+        minWidth: 100,
+        render: (val) => (val.isPoint === YES_OR_NO.YES ? "是" : "否"),
+      },
+      {
+        title: "责任部门",
+        align: "center",
+        minWidth: 120,
+        dataIndex: "deptName",
+      },
+      {
+        title: "评定得分",
+        align: "center",
+        minWidth: 120,
+        render: (val) => val.score || <p className="empty-text">(空)</p>,
+      },
+      {
+        title: "评定人",
+        align: "center",
+        minWidth: 100,
+        render: (val) => <p className="empty-text">(空)</p>,
+      },
+      {
+        title: "评定意见",
+        align: "center",
+        minWidth: 120,
+        render: (val) => <p className="empty-text">(空)</p>,
+      },
+      {
+        title: "操作",
+        align: "center",
+        hidden: !isEvalutionDetail,
+        render: () => (
+          <Button
+            type="link"
+            onClick={() => navigate("/management/evaluation/detail/submit")}
+          >
+            评定
+          </Button>
+        ),
+      },
+    ];
+
+    if (
+      isReportDetail &&
+      (detail as IManageFormDetail)?.isSelf === YES_OR_NO.YES
+    ) {
+      stack.splice(
+        7,
+        0,
+        {
+          title: "自评得分",
+          align: "center",
+          minWidth: 120,
+          render: (val) =>
+            !val.selfScore ? (
+              <Button
+                type="text"
+                color="primary"
+                variant="link"
+                onClick={() => {
+                  setCheckedItem(val);
+                  setSelfReportVisible(true);
+                }}
+              >
+                (空)
+              </Button>
+            ) : (
+              val.selfScore
+            ),
+        },
+        {
+          title: "自评人",
+          align: "center",
+          minWidth: 100,
+          render: (val) => val.selfName || <p className="empty-text">(空)</p>,
+        }
+      );
+    }
+
+    return stack;
+  }, [detail]);
+
+  const getList = async () => {
+    try {
+      setLoading(true);
+      const data = await getManageNormListApi({
+        deptId: routeParams.id as string,
+        ...params,
+      });
+      setList(data);
+    } finally {
+      setLoading(false);
+    }
+  };
+
+  const debounceSearch = useMemo(
+    () =>
+      debounce((changedVal: unknown, vals: any) => {
+        setParams({ ...params, ...vals });
+      }, 500),
+    [params]
+  );
+
+  const handleSelfScoreCancel = () => {
+    setCheckedItem(null);
+    setSelfReportVisible(false);
+  };
+
+  useEffect(() => {
+    getList();
+  }, []);
 
   return (
     <>
-      <Form layout="inline">
-        <Form.Item label="搜索">
-          <Input className="w220" placeholder="请输入指标名称" />
+      <Form layout="inline" onValuesChange={debounceSearch}>
+        <Form.Item label="搜索" name="searchKey">
+          <Input allowClear className="w220" placeholder="请输入指标名称" />
         </Form.Item>
         <Form.Item label="上传状态">
           <div className="w160">
-            <Select placeholder="请选择" />
+            <Form.Item noStyle name="uploadStatus">
+              <Select
+                allowClear
+                options={CONFIRM_OPTIONS}
+                placeholder="请选择"
+              />
+            </Form.Item>
           </div>
         </Form.Item>
-        <Form.Item label="责任部门">
+        {/* <Form.Item label="责任部门">
           <div className="w160">
             <Select placeholder="请选择" />
           </div>
-        </Form.Item>
+        </Form.Item> */}
         <Form.Item>
-          <Button type="primary">查询</Button>
+          <Button type="primary" onClick={getList}>
+            查询
+          </Button>
         </Form.Item>
       </Form>
 
@@ -41,129 +258,31 @@ export const IndexAssessment: FC<IndexAssessmentProps> = ({
           scroll={{
             x: true,
           }}
-          dataSource={[
-            {
-              id: 1,
-              name: "综合管理与基础设施",
-              description:
-                "消息一出,瞬间引爆互联网,分分钟登顶Hacker News。假如这次发现为真,那么我们就能实现无损的能量传输,全球的能耗问题将从源头上解决,人类能利用电能获得巨大的力量。",
-            },
-          ]}
-          columns={[
-            {
-              title: "序号",
-              align: "center",
-              minWidth: 80,
-              render: (val, record, index) => index,
-            },
-            {
-              title: "标题",
-              dataIndex: "name",
-              align: "center",
-              minWidth: 200,
-            },
-            {
-              title: "说明",
-              minWidth: 300,
-              align: "center",
-              render: (val) => (
-                <p className="limit-line line-2">{val.description}</p>
-              ),
-            },
-            {
-              title: "分值",
-              align: "center",
-              render: (val) => <p className="empty-text">(空)</p>,
-            },
-            {
-              title: "资料上传 ",
-              align: "center",
-              render: () => (
-                <Button
-                  type="link"
-                  onClick={() =>
-                    navigate(
-                      isReportDetail
-                        ? // 考核填报
-                          "/management/form/detail/1/index"
-                        : isEvalutionDetail
-                        ? // 考核评定
-                          "/management/evaluation/detail/1/index"
-                        : // 考核管理
-                          "/management/index/detail/1/index"
-                    )
-                  }
-                >
-                  查看
-                </Button>
-              ),
-            },
-            {
-              title: "上传状态",
-              align: "center",
-              minWidth: 120,
-              render: (val) => <p className="empty-text">(空)</p>,
-            },
-            {
-              title: "打分点",
-              align: "center",
-              minWidth: 100,
-              render: (val) => "是",
-            },
-            {
-              title: "责任部门",
-              align: "center",
-              minWidth: 120,
-              render: (val) => <p className="empty-text">(空)</p>,
-            },
-            {
-              title: "自评得分",
-              dataIndex: "rate",
-              align: "center",
-              minWidth: 120,
-            },
-            {
-              title: "自评人",
-              align: "center",
-              minWidth: 100,
-              render: (val) => <p className="empty-text">(空)</p>,
-            },
-            {
-              title: "评定得分",
-              dataIndex: "rates",
-              align: "center",
-              minWidth: 120,
-            },
-            {
-              title: "评定人",
-              align: "center",
-              minWidth: 100,
-              render: (val) => <p className="empty-text">(空)</p>,
-            },
-            {
-              title: "评定意见",
-              align: "center",
-              minWidth: 120,
-              render: (val) => <p className="empty-text">(空)</p>,
-            },
-            {
-              title: "操作",
-              align: "center",
-              hidden: !isEvalutionDetail,
-              render: () => (
-                <Button
-                  type="link"
-                  onClick={() =>
-                    navigate("/management/evaluation/detail/submit")
-                  }
-                >
-                  评定
-                </Button>
-              ),
-            },
-          ]}
+          loading={loading}
+          dataSource={list}
+          columns={columns}
         />
       </div>
+
+      <IndexDetailModal
+        open={indexDetailVisible}
+        id={checkedItem?.normId || 0}
+        title={checkedItem?.name || ""}
+        onCancel={() => {
+          setCheckedItem(null);
+          setIndexDetailVisible(false);
+        }}
+      />
+
+      <SelfReportScoreModal
+        open={selfReportVisible}
+        item={checkedItem}
+        onOk={() => {
+          getList();
+          handleSelfScoreCancel();
+        }}
+        onCancel={handleSelfScoreCancel}
+      />
     </>
   );
 };

+ 15 - 0
src/pages/AssessmentDetail/components/IndexDetailModal/index.module.scss

@@ -0,0 +1,15 @@
+.fileUploadHeader {
+  display: flex;
+  align-items: center;
+  gap: 10px;
+  margin: 20px 0;
+}
+.fileUploadHeaderInfo {
+  flex: 1;
+  display: flex;
+  align-items: center;
+}
+div.uploadBtn {
+  flex: unset;
+  min-width: unset;
+}

+ 236 - 0
src/pages/AssessmentDetail/components/IndexDetailModal/index.tsx

@@ -0,0 +1,236 @@
+import { FC, useEffect, useState } from "react";
+import { Button, Empty, Modal, ModalProps, Table, Tag } from "antd";
+import { MaterialType, YES_OR_NO } from "@/types";
+import { getBaseURL } from "@dage/service";
+import { deleteFileApi, getFileListApi, saveManageFileApi } from "@/api";
+import { downloadFile, beforeUpload } from "@/utils";
+import {
+  DageFileResponseType,
+  DageLoading,
+  DageTableActions,
+  DageUpload,
+  DageUploadConsumer,
+  DageUploadProvider,
+  DageUploadType,
+} from "@dage/pc-components";
+import classNames from "classnames";
+import style from "./index.module.scss";
+
+export interface IndexDetailModalProps extends Omit<ModalProps, "onOk"> {
+  id: number;
+  title: string;
+  onCancel?: () => void;
+  onOk?: (val: any) => void;
+}
+
+export const IndexDetailModal: FC<IndexDetailModalProps> = ({
+  open,
+  id,
+  title,
+  onOk,
+  onCancel,
+  ...rest
+}) => {
+  const baseUrl = getBaseURL();
+  const [loading, setLoading] = useState(false);
+  const [list, setList] = useState<MaterialType[]>([]);
+  // 已上传附件映射
+  const [uploadedFileMap, setUploadedFileMap] = useState<
+    Record<number, MaterialType[]>
+  >({});
+
+  const handleCancel = () => {
+    onCancel?.();
+  };
+
+  const saveManageFile = async (
+    item: MaterialType,
+    list: DageFileResponseType[]
+  ) => {
+    // @ts-ignore
+    const li = list.filter((i) => i.status === "done" && !i.uploaded);
+
+    for (let i = 0; i < li.length; i++) {
+      const file = li[i];
+
+      if (!file.response) return;
+
+      await saveManageFileApi({
+        name: file.name,
+        fileName: file.response.fileName,
+        filePath: file.response.filePath,
+        level: 2,
+        suffix: item.suffix,
+        parentId: item.id,
+        module: "fill-norm",
+        moduleId: id,
+      });
+      // @ts-ignore
+      file.uploaded = true;
+    }
+
+    getList();
+  };
+
+  const getList = async () => {
+    try {
+      setLoading(true);
+      const data = await getFileListApi(id, "norm");
+      setList(data.filter((i) => i.module === "norm"));
+
+      const temp: typeof uploadedFileMap = {};
+      data
+        .filter((i) => i.module === "fill-norm")
+        .forEach((item) => {
+          if (!item.parentId) return;
+          if (Array.isArray(temp[item.parentId]))
+            temp[item.parentId].push(item);
+          else temp[item.parentId] = [item];
+        });
+      setUploadedFileMap(temp);
+    } finally {
+      setLoading(false);
+    }
+  };
+
+  const handleDeleteFile = async (id: number) => {
+    await deleteFileApi(id);
+    getList();
+  };
+
+  useEffect(() => {
+    if (open) getList();
+  }, [open]);
+
+  return (
+    <Modal
+      className={style.modal}
+      title={title}
+      open={open}
+      width={900}
+      footer={null}
+      onCancel={handleCancel}
+      {...rest}
+    >
+      {loading && <DageLoading />}
+
+      {!list.length && <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />}
+
+      {list.map((item) => {
+        const isUpload = item.isUpload === YES_OR_NO.YES;
+
+        return (
+          <div key={item.id} className={style.fileUpload}>
+            <div className={style.fileUploadHeader}>
+              <div className={style.fileUploadHeaderInfo}>
+                <Tag color={isUpload ? "red" : ""}>
+                  {isUpload ? "必填" : "选填"}
+                </Tag>
+                <h3>
+                  {item.name} | {item.suffix || "*"}
+                </h3>
+              </div>
+              <Button
+                type="primary"
+                ghost
+                onClick={() =>
+                  downloadFile(
+                    baseUrl + process.env.REACT_APP_IMG_PUBLIC + item.filePath,
+                    item.fileName
+                  )
+                }
+              >
+                下载模板
+              </Button>
+              <DageUploadProvider>
+                <DageUploadConsumer>
+                  {(res) => (
+                    <DageUpload
+                      className={classNames(style.uploadBtn, "no-list")}
+                      dType={DageUploadType.DOC}
+                      action="/api/cms/fill/file/upload"
+                      // @ts-ignore
+                      accept={item.suffix}
+                      // @ts-ignore
+                      beforeUpload={beforeUpload.bind(undefined, item.suffix)}
+                      onChange={saveManageFile.bind(undefined, item)}
+                    >
+                      <Button type="primary" ghost loading={res?.uploading}>
+                        上传附件
+                      </Button>
+                    </DageUpload>
+                  )}
+                </DageUploadConsumer>
+              </DageUploadProvider>
+            </div>
+
+            <Table
+              rowKey="id"
+              className="cus-table"
+              pagination={false}
+              dataSource={uploadedFileMap[item.id]}
+              columns={[
+                {
+                  title: "附件名称",
+                  dataIndex: "fileName",
+                  align: "center",
+                },
+                {
+                  title: "责任部门",
+                  dataIndex: "deptName",
+                  align: "center",
+                },
+                {
+                  title: "上传用户",
+                  dataIndex: "creatorName",
+                  align: "center",
+                },
+                {
+                  title: "上传时间",
+                  dataIndex: "updateTime",
+                  align: "center",
+                },
+                {
+                  title: "评定结果",
+                  dataIndex: "creatorName",
+                  align: "center",
+                },
+                {
+                  title: "评定意见",
+                  dataIndex: "updateTime",
+                  align: "center",
+                },
+                {
+                  title: "操作",
+                  align: "center",
+                  render: (val) => (
+                    <DageTableActions
+                      renderBefore={
+                        <Button
+                          size="small"
+                          type="link"
+                          onClick={() =>
+                            downloadFile(
+                              baseUrl +
+                                process.env.REACT_APP_IMG_PUBLIC +
+                                item.filePath,
+                              item.fileName
+                            )
+                          }
+                        >
+                          下载
+                        </Button>
+                      }
+                      showEdit={false}
+                      onDelete={handleDeleteFile.bind(undefined, val.id)}
+                    />
+                  ),
+                },
+              ]}
+            />
+          </div>
+        );
+      })}
+    </Modal>
+  );
+};

+ 11 - 0
src/pages/AssessmentDetail/components/OverallAssessment/index.module.scss

@@ -13,6 +13,12 @@
   }
 }
 
+.fileUpload {
+  &:not(:last-child) {
+    margin-bottom: 20px;
+  }
+}
+
 .fileUploadHeader {
   display: flex;
   align-items: center;
@@ -25,3 +31,8 @@
     align-items: center;
   }
 }
+
+div.uploadBtn {
+  flex: unset;
+  min-width: unset;
+}

+ 133 - 12
src/pages/AssessmentDetail/components/OverallAssessment/index.tsx

@@ -1,12 +1,26 @@
 import { Button, Form, Input, Table, Tag } from "antd";
-import { FC, useMemo, useState } from "react";
+import { FC, useEffect, useMemo, useState } from "react";
 import classNames from "classnames";
-import { DageTableActions } from "@dage/pc-components";
-import { EvaluationModal } from "../EvaluationModal";
+import {
+  DageFileResponseType,
+  DageTableActions,
+  DageUpload,
+  DageUploadConsumer,
+  DageUploadProvider,
+  DageUploadType,
+} from "@dage/pc-components";
+import { SubEvaluationModal } from "../SubEvaluationModal";
 import style from "./index.module.scss";
-import { IManageFormDetail, IManageIndexDetail, YES_OR_NO } from "@/types";
-import { downloadFile } from "@/utils";
+import {
+  IManageFormDetail,
+  IManageIndexDetail,
+  MaterialType,
+  PUBLISH_ENUM,
+  YES_OR_NO,
+} from "@/types";
+import { downloadFile, beforeUpload } from "@/utils";
 import { getBaseURL } from "@dage/service";
+import { deleteFileApi, getFileListApi, saveManageFileApi } from "@/api";
 
 export interface OverallAssessmentProps {
   detail: IManageIndexDetail | IManageFormDetail | null;
@@ -22,6 +36,12 @@ export const OverallAssessment: FC<OverallAssessmentProps> = ({
   isEvalutionDetail,
 }) => {
   const baseUrl = getBaseURL();
+  // 待填报
+  const toBeFilled =
+    (detail as IManageFormDetail)?.publishStatus === PUBLISH_ENUM.PUBLISHED;
+  // 已评定
+  const evaluated =
+    (detail as IManageFormDetail)?.publishStatus === PUBLISH_ENUM.EVALUATED;
   const [modalVisible, setModelVisible] = useState(false);
   // 加分项
   const addList = useMemo(() => {
@@ -33,6 +53,69 @@ export const OverallAssessment: FC<OverallAssessmentProps> = ({
     if (!detail || !detail.jsonSub) return [];
     return JSON.parse(detail.jsonSub);
   }, [detail]);
+  const [loading, setLoading] = useState(false);
+  // 已上传附件映射
+  const [uploadedFileMap, setUploadedFileMap] = useState<
+    Record<number, MaterialType[]>
+  >({});
+
+  const getList = async () => {
+    try {
+      setLoading(true);
+      const data = await getFileListApi(
+        (detail as IManageFormDetail).accessId,
+        "access"
+      );
+      const temp: typeof uploadedFileMap = {};
+      data.forEach((item) => {
+        if (!item.parentId) return;
+        if (Array.isArray(temp[item.parentId])) temp[item.parentId].push(item);
+        else temp[item.parentId] = [item];
+      });
+      setUploadedFileMap(temp);
+    } finally {
+      setLoading(false);
+    }
+  };
+
+  const saveManageFile = async (
+    item: MaterialType,
+    list: DageFileResponseType[]
+  ) => {
+    // @ts-ignore
+    const li = list.filter((i) => i.status === "done" && !i.uploaded);
+
+    for (let i = 0; i < li.length; i++) {
+      const file = li[i];
+
+      if (!file.response) return;
+
+      await saveManageFileApi({
+        name: file.name,
+        fileName: file.response.fileName,
+        filePath: file.response.filePath,
+        level: 2,
+        suffix: item.suffix,
+        parentId: item.id,
+        module: "fill-assess",
+        moduleId: (detail as IManageFormDetail).accessId,
+      });
+      // @ts-ignore
+      file.uploaded = true;
+    }
+
+    getList();
+  };
+
+  const handleDeleteFile = async (id: number) => {
+    await deleteFileApi(id);
+    getList();
+  };
+
+  useEffect(() => {
+    if (!detail) return;
+    getList();
+  }, [detail]);
 
   return (
     <Form
@@ -44,7 +127,7 @@ export const OverallAssessment: FC<OverallAssessmentProps> = ({
       className={classNames(style.form, "mw1125")}
     >
       <Form.Item label="考核说明">
-        <p>{detail?.remark}</p>
+        <p>{detail?.remark || "--"}</p>
       </Form.Item>
 
       {!isReportDetail && (
@@ -97,14 +180,14 @@ export const OverallAssessment: FC<OverallAssessmentProps> = ({
           const isUpload = item.isUpload === YES_OR_NO.YES;
 
           return (
-            <div key={item.id}>
+            <div key={item.id} className={style.fileUpload}>
               <div className={style.fileUploadHeader}>
                 <div>
                   <Tag color={isUpload ? "red" : ""}>
                     {isUpload ? "必填" : "选填"}
                   </Tag>
                   <h3>
-                    {item.name} | {item.suffix}
+                    {item.name} | {item.suffix || "*"}
                   </h3>
                 </div>
                 <Button
@@ -121,13 +204,39 @@ export const OverallAssessment: FC<OverallAssessmentProps> = ({
                 >
                   下载模板
                 </Button>
+                {toBeFilled && !isEvalutionDetail && (
+                  <DageUploadProvider>
+                    <DageUploadConsumer>
+                      {(res) => (
+                        <DageUpload
+                          className={classNames(style.uploadBtn, "no-list")}
+                          dType={DageUploadType.DOC}
+                          action="/api/cms/fill/file/upload"
+                          // @ts-ignore
+                          accept={item.suffix}
+                          // @ts-ignore
+                          beforeUpload={beforeUpload.bind(
+                            undefined,
+                            item.suffix
+                          )}
+                          onChange={saveManageFile.bind(undefined, item)}
+                        >
+                          <Button type="primary" ghost loading={res?.uploading}>
+                            上传附件
+                          </Button>
+                        </DageUpload>
+                      )}
+                    </DageUploadConsumer>
+                  </DageUploadProvider>
+                )}
               </div>
 
               <Table
                 rowKey="id"
+                loading={loading}
                 className="cus-table"
                 pagination={false}
-                dataSource={[]}
+                dataSource={uploadedFileMap[item.id]}
                 columns={[
                   {
                     title: "附件名称",
@@ -152,15 +261,27 @@ export const OverallAssessment: FC<OverallAssessmentProps> = ({
                   {
                     title: "操作",
                     align: "center",
-                    render: () => (
+                    render: (val) => (
                       <DageTableActions
                         renderBefore={
-                          <Button size="small" type="link">
+                          <Button
+                            size="small"
+                            type="link"
+                            onClick={() => {
+                              downloadFile(
+                                baseUrl +
+                                  process.env.REACT_APP_IMG_PUBLIC +
+                                  val.filePath,
+                                val.fileName
+                              );
+                            }}
+                          >
                             下载
                           </Button>
                         }
                         showDelete={isReportDetail}
                         showEdit={false}
+                        onDelete={handleDeleteFile.bind(undefined, val.id)}
                       />
                     ),
                   },
@@ -260,7 +381,7 @@ export const OverallAssessment: FC<OverallAssessmentProps> = ({
         </Form.Item>
       )}
 
-      <EvaluationModal
+      <SubEvaluationModal
         open={modalVisible}
         onCancel={() => setModelVisible(false)}
       />

+ 80 - 0
src/pages/AssessmentDetail/components/SelfReportScoreModal/index.tsx

@@ -0,0 +1,80 @@
+import { FC, useState } from "react";
+import { Form, Input, InputNumber, Modal, ModalProps } from "antd";
+import style from "@/components/AddIndexModal/index.module.scss";
+import { IManageNormItem } from "@/types";
+import { saveSelfScoreApi } from "@/api";
+
+export interface SelfReportScoreModalProps extends Omit<ModalProps, "onOk"> {
+  item: IManageNormItem | null;
+  onCancel?: () => void;
+  onOk?: () => void;
+}
+
+export const SelfReportScoreModal: FC<SelfReportScoreModalProps> = ({
+  open,
+  item,
+  onOk,
+  onCancel,
+  ...rest
+}) => {
+  const [form] = Form.useForm<any>();
+  const [loading, setLoading] = useState(false);
+
+  const handleCancel = () => {
+    form.resetFields();
+    onCancel?.();
+  };
+
+  const handleConfirm = () => {
+    form.submit();
+  };
+
+  const handleSubmit = async (values: any) => {
+    try {
+      setLoading(true);
+      await saveSelfScoreApi(item!.id, values.score);
+      form.resetFields();
+      onOk?.();
+    } finally {
+      setLoading(false);
+    }
+  };
+
+  return (
+    <Modal
+      className={style.modal}
+      title={item?.name}
+      okText="提交"
+      cancelText="取消"
+      open={open}
+      width={640}
+      okButtonProps={{
+        disabled: loading,
+      }}
+      onOk={handleConfirm}
+      onCancel={handleCancel}
+      {...rest}
+    >
+      <Form
+        labelCol={{ span: 4, offset: 2 }}
+        form={form}
+        onFinish={handleSubmit}
+      >
+        <Form.Item
+          label="自评得分"
+          name="score"
+          required
+          rules={[{ required: true, message: "请输入 " }]}
+        >
+          <InputNumber
+            className="w100"
+            min={0}
+            max={50}
+            precision={1}
+            placeholder="请输入0~50的数字,支持小数点后一位"
+          />
+        </Form.Item>
+      </Form>
+    </Modal>
+  );
+};

+ 2 - 2
src/pages/AssessmentDetail/components/EvaluationModal/index.tsx

@@ -2,14 +2,14 @@ import { FC } from "react";
 import { Form, Input, InputNumber, Modal, ModalProps } from "antd";
 import style from "@/components/AddIndexModal/index.module.scss";
 
-export interface EvaluationModalProps extends Omit<ModalProps, "onOk"> {
+export interface SubEvaluationModalProps extends Omit<ModalProps, "onOk"> {
   onCancel?: () => void;
   onOk?: (val: any) => void;
 }
 
 const { TextArea } = Input;
 
-export const EvaluationModal: FC<EvaluationModalProps> = ({
+export const SubEvaluationModal: FC<SubEvaluationModalProps> = ({
   open,
   onOk,
   onCancel,

+ 89 - 42
src/pages/AssessmentDetail/index.tsx

@@ -6,13 +6,18 @@ import { IndexAssessment } from "./components/IndexAssessment";
 import style from "./index.module.scss";
 import { useHashQuery } from "@/hooks";
 import { DEPT_STATUS_MAP, PUBLISH_STATUS_MAP } from "@/constants";
-import { useParams } from "react-router-dom";
+import { useNavigate, useParams } from "react-router-dom";
 import {
   getManageEvaluationDetailApi,
   getManageFormDetailApi,
   getManageIndexDetailApi,
 } from "@/api";
-import { IManageFormDetail, IManageIndexDetail } from "@/types";
+import {
+  DEPT_STATUS_ENUM,
+  IManageFormDetail,
+  IManageIndexDetail,
+  YES_OR_NO,
+} from "@/types";
 import { DageLoading } from "@dage/pc-components";
 import { isNumber } from "lodash";
 
@@ -31,15 +36,32 @@ const AssessmentDetailPage: FC = () => {
     IManageIndexDetail | IManageFormDetail | null
   >(null);
   const params = useParams();
+  const navigate = useNavigate();
+  /** 考核单状态 */
+  const status = detail
+    ? isNumber((detail as IManageIndexDetail).status)
+      ? (detail as IManageIndexDetail).status
+      : (detail as IManageFormDetail).publishStatus
+    : null;
+  // 当前账号是否为该考核单负责人
+  const isLeader =
+    (detail as IManageFormDetail)?.leaderUserId === YES_OR_NO.YES;
 
   const getDetail = async () => {
     try {
       setLoading(true);
-      const data = await (isReportDetail.current
-        ? getManageFormDetailApi
-        : isEvalutionDetail.current
-        ? getManageEvaluationDetailApi
-        : getManageIndexDetailApi)(params.id as string);
+
+      let data: IManageIndexDetail | IManageFormDetail | null = null;
+      if (isReportDetail.current) {
+        data = await getManageFormDetailApi(
+          params.id as string,
+          params.accessId as string
+        );
+      } else {
+        data = await (isEvalutionDetail.current
+          ? getManageEvaluationDetailApi
+          : getManageIndexDetailApi)(params.id as string);
+      }
       setDetail(data);
     } finally {
       setLoading(false);
@@ -61,33 +83,11 @@ const AssessmentDetailPage: FC = () => {
           boxShadow: "0px 0px 10px 0px rgba(0,0,0,0.05)",
         }}
         headerSlot={
-          detail ? (
+          status ? (
             <div style={{ flex: 1 }}>
-              {!isIndexDetail.current ? (
-                <Tag
-                  color={
-                    DEPT_STATUS_MAP[(detail as IManageFormDetail).deptStatus]
-                      .color
-                  }
-                >
-                  {
-                    DEPT_STATUS_MAP[(detail as IManageFormDetail).deptStatus]
-                      .label
-                  }
-                </Tag>
-              ) : (
-                <Tag
-                  color={
-                    PUBLISH_STATUS_MAP[(detail as IManageIndexDetail).status]
-                      .color
-                  }
-                >
-                  {
-                    PUBLISH_STATUS_MAP[(detail as IManageIndexDetail).status]
-                      .label
-                  }
-                </Tag>
-              )}
+              <Tag color={PUBLISH_STATUS_MAP[status].color}>
+                {PUBLISH_STATUS_MAP[status].label}
+              </Tag>
             </div>
           ) : undefined
         }
@@ -112,6 +112,26 @@ const AssessmentDetailPage: FC = () => {
               <label>{detail?.creatorName}</label>
               <span style={{ fontWeight: "normal" }}>{detail?.createTime}</span>
             </div>
+            {isReportDetail.current && detail && (
+              <div
+                className={style.topContentItem}
+                style={{ display: "flex", alignItems: "center", gap: 10 }}
+              >
+                <span>{(detail as IManageFormDetail).deptName}</span>
+                <p>|</p>
+                <Tag
+                  color={
+                    DEPT_STATUS_MAP[(detail as IManageFormDetail).deptStatus]
+                      .color
+                  }
+                >
+                  {
+                    DEPT_STATUS_MAP[(detail as IManageFormDetail).deptStatus]
+                      .label
+                  }
+                </Tag>
+              </div>
+            )}
           </div>
         </div>
       </PageContainer>
@@ -141,6 +161,7 @@ const AssessmentDetailPage: FC = () => {
               label: "指标考核",
               children: (
                 <IndexAssessment
+                  detail={detail}
                   isReportDetail={isReportDetail.current}
                   isEvalutionDetail={isEvalutionDetail.current}
                 />
@@ -151,14 +172,38 @@ const AssessmentDetailPage: FC = () => {
         />
       </div>
 
-      {isReportDetail.current && (
-        <FormPageFooter>
-          <Button size="large" type="primary">
-            提交
-          </Button>
-          <Button size="large">保存并关闭</Button>
-        </FormPageFooter>
-      )}
+      {isReportDetail.current &&
+        [
+          DEPT_STATUS_ENUM.PENDING_SUBMIT,
+          DEPT_STATUS_ENUM.RETURN,
+          DEPT_STATUS_ENUM.EXAMINE_REJECT,
+        ].includes((detail as IManageFormDetail)?.deptStatus) && (
+          <FormPageFooter>
+            <Button size="large" type="primary">
+              提交
+            </Button>
+            <Button size="large" onClick={() => navigate(-1)}>
+              保存并关闭
+            </Button>
+          </FormPageFooter>
+        )}
+
+      {isReportDetail.current &&
+        isLeader &&
+        (detail as IManageFormDetail)?.deptStatus ===
+          DEPT_STATUS_ENUM.PENDING_EXAMINE && (
+          <FormPageFooter>
+            <Button size="large" type="primary">
+              审核通过
+            </Button>
+            <Button size="large" color="danger">
+              审核驳回
+            </Button>
+            <Button size="large" onClick={() => navigate(-1)}>
+              关闭
+            </Button>
+          </FormPageFooter>
+        )}
 
       {isEvalutionDetail.current && (
         <FormPageFooter>
@@ -171,7 +216,9 @@ const AssessmentDetailPage: FC = () => {
           <Button size="large" danger style={{ width: 160 }}>
             退回不合格的资料
           </Button>
-          <Button size="large">关闭</Button>
+          <Button size="large" onClick={() => navigate(-1)}>
+            关闭
+          </Button>
         </FormPageFooter>
       )}
     </>

+ 11 - 10
src/pages/Management/Evaluation/index.tsx

@@ -9,7 +9,7 @@ import {
   PUBLISH_STATUS_OPTIONS,
 } from "../../../constants";
 import style from "../Form/index.module.scss";
-import { debounce } from "lodash";
+import { debounce, isNumber } from "lodash";
 import { getManageEvaluationListApi } from "@/api";
 import { IManageFormItem, IManageFormListParams } from "@/types";
 
@@ -143,15 +143,16 @@ const ManagementEvaluationPage = () => {
               key: "c",
               align: "center",
               minWidth: 100,
-              render: (item: IManageFormItem) => (
-                <p
-                  style={{
-                    color: PUBLISH_STATUS_MAP[item.publishStatus].color,
-                  }}
-                >
-                  {PUBLISH_STATUS_MAP[item.publishStatus].label}
-                </p>
-              ),
+              render: (item: IManageFormItem) =>
+                isNumber(item.publishStatus) ? (
+                  <p
+                    style={{
+                      color: PUBLISH_STATUS_MAP[item.publishStatus].color,
+                    }}
+                  >
+                    {PUBLISH_STATUS_MAP[item.publishStatus].label}
+                  </p>
+                ) : undefined,
             },
             {
               title: "操作",

+ 58 - 17
src/pages/Management/Files/index.tsx

@@ -1,25 +1,54 @@
 import classNames from "classnames";
-import { useState } from "react";
+import { useCallback, useEffect, useMemo, useState } from "react";
 import { Button, Form, Input, Select, Table } from "antd";
 import { PageContainer } from "@/components";
 import style from "./index.module.scss";
-import { PUBLISH_ENUM } from "@/types";
+import { debounce } from "lodash";
+import { getManageFileListAPi } from "@/api";
+
+const DEFAULT_PARAMS = {
+  pageNum: 1,
+  pageSize: 20,
+  searchKey: "",
+};
 
 const ManagementReportPage = () => {
-  const [list] = useState([
-    {
-      id: 1,
-      name: "模板名称",
-      a: "定级评估",
-      description: "模板说明",
-      b: "",
-      c: PUBLISH_ENUM.PENDING,
-      d: "",
-      e: "",
-      f: "2024-09-24 16:50:30",
-      g: "钱韵澄",
+  const [params, setParams] = useState({
+    ...DEFAULT_PARAMS,
+  });
+  const [loading, setLoading] = useState(false);
+  const [total, setTotal] = useState(0);
+  const [list, setList] = useState<[]>([]);
+
+  const debounceSearch = useMemo(
+    () =>
+      debounce((changedVal: unknown, vals: any) => {
+        setParams({ ...params, ...vals });
+      }, 500),
+    [params]
+  );
+
+  const paginationChange = useCallback(
+    () => (pageNum: number, pageSize: number) => {
+      setParams({ ...params, pageNum, pageSize });
     },
-  ]);
+    [params]
+  );
+
+  const getList = useCallback(async () => {
+    setLoading(true);
+    try {
+      const data = await getManageFileListAPi(params);
+      setList(data.records);
+      setTotal(data.total);
+    } finally {
+      setLoading(false);
+    }
+  }, [params]);
+
+  useEffect(() => {
+    getList();
+  }, []);
 
   return (
     <PageContainer title="附件管理">
@@ -34,13 +63,16 @@ const ManagementReportPage = () => {
             </div>
           </Form.Item>
           <Form.Item>
-            <Button type="primary">查询</Button>
+            <Button type="primary" onClick={getList}>
+              查询
+            </Button>
           </Form.Item>
         </Form>
       </div>
 
       <div className={style.table}>
         <Table
+          loading={loading}
           className={classNames("cus-table large")}
           dataSource={list}
           rowKey="id"
@@ -107,7 +139,7 @@ const ManagementReportPage = () => {
               key: "h",
               align: "center",
               fixed: "right",
-              render: (item: (typeof list)[0]) => {
+              render: (item: any) => {
                 return (
                   <>
                     <Button type="link">下载</Button>
@@ -119,6 +151,15 @@ const ManagementReportPage = () => {
               },
             },
           ]}
+          pagination={{
+            showQuickJumper: true,
+            position: ["bottomCenter"],
+            showSizeChanger: true,
+            current: params.pageNum,
+            pageSize: params.pageSize,
+            total,
+            onChange: paginationChange(),
+          }}
         />
       </div>
     </PageContainer>

+ 7 - 5
src/pages/Management/Form/index.tsx

@@ -160,10 +160,10 @@ const ManagementReportPage = () => {
                 return (
                   <p
                     style={{
-                      color: PUBLISH_STATUS_MAP[item.publishStatus].color,
+                      color: PUBLISH_STATUS_MAP[item.publishStatus || 0].color,
                     }}
                   >
-                    {PUBLISH_STATUS_MAP[item.publishStatus].label}
+                    {PUBLISH_STATUS_MAP[item.publishStatus || 0].label}
                   </p>
                 );
               },
@@ -195,16 +195,18 @@ const ManagementReportPage = () => {
               align: "center",
               fixed: "right",
               render: (item: IManageFormItem) => {
-                return (
+                return item.accessId ? (
                   <Button
                     type="link"
                     onClick={() =>
-                      navigate(`/management/form/detail/${item.id}`)
+                      navigate(
+                        `/management/form/detail/${item.id}/${item.accessId}`
+                      )
                     }
                   >
                     查看
                   </Button>
-                );
+                ) : undefined;
               },
             },
           ]}

+ 110 - 78
src/pages/Management/Index/SettingIndex/index.tsx

@@ -1,5 +1,5 @@
-import { FC, Key, useEffect, useRef, useState } from "react";
-import { Button, Form, Space, Table } from "antd";
+import { FC, useEffect, useRef, useState } from "react";
+import { Button, Form, InputNumber, Space, Table } from "antd";
 import { useParams } from "react-router-dom";
 import { ActionType, EditableProTable } from "@ant-design/pro-components";
 import { PlusOutlined } from "@ant-design/icons";
@@ -10,12 +10,14 @@ import {
   Search,
 } from "@/components";
 // import { uniq, uniqBy } from "lodash";
-import { ASS_INDEX_TYPE, IManageAssessmentIndex } from "@/types";
+import { ASS_INDEX_TYPE } from "@/types";
 import style from "./index.module.scss";
 import {
   getManageAssFixedListApi,
   getManageAssOperationListApi,
+  saveManageAssOperationWeightApi,
   setManageAssFixedApi,
+  setManageAssOperationApi,
 } from "@/api";
 import { DageLoading } from "@dage/pc-components";
 
@@ -28,7 +30,6 @@ const SettingIndexPage: FC = () => {
   const [indexModalVisible, setIndexModalVisible] = useState(false);
   const [indexTemplateModalVisible, setIndexTemplateModalVisible] =
     useState(false);
-  const [editableKeys, setEditableKeys] = useState<Key[]>([]);
   const [loading, setLoading] = useState(false);
   // 设置定级指标
   const isFixed = params.type === ASS_INDEX_TYPE.FIXED;
@@ -54,6 +55,7 @@ const SettingIndexPage: FC = () => {
             ...data.gist,
           },
         ]);
+        form.setFieldValue("list", data.list);
       }
     } finally {
       setLoading(false);
@@ -63,6 +65,8 @@ const SettingIndexPage: FC = () => {
   const handleAddIndexItem = async (keys: string[]) => {
     if (isFixed) {
       await setManageAssFixedApi(params.id!, keys);
+    } else {
+      await setManageAssOperationApi(params.id!, keys);
     }
     getList();
   };
@@ -86,8 +90,6 @@ const SettingIndexPage: FC = () => {
     getList("");
   };
 
-  // const handleDelete = (item: IManageAssessmentIndex) => {};
-
   useEffect(() => {
     getList();
   }, []);
@@ -134,79 +136,111 @@ const SettingIndexPage: FC = () => {
         </Form.Item>
         <Form.Item required label="考核指标">
           <Form.Item noStyle name="list">
-            <EditableProTable<IManageAssessmentIndex>
+            <EditableProTable
               className="custom-pro-table mw650"
               actionRef={actionRef}
               // editableFormRef={tableRef}
               rowKey="id"
               style={{ marginTop: -15 }}
               recordCreatorProps={false}
-              columns={[
-                {
-                  title: "指标级别",
-                  dataIndex: "level",
-                  align: "center",
-                  editable: false,
-                },
-                {
-                  title: "指标名称",
-                  dataIndex: "name",
-                  align: "center",
-                  editable: false,
-                },
-                {
-                  title: "填报方式",
-                  dataIndex: "fill",
-                  align: "center",
-                  width: "230px",
-                  // renderFormItem: () => (
-                  //   <Radio.Group>
-                  //     <Radio value="point">手动填报</Radio>
-                  //     <Radio value="api">
-                  //       API{" "}
-                  //       <Tooltip title="以 type,start_date,end_date请求API数据。当API获取数据失败时,将自动转为手动填报">
-                  //         <Tag bordered={false} color="warning">
-                  //           注
-                  //         </Tag>
-                  //       </Tooltip>
-                  //     </Radio>
-                  //   </Radio.Group>
-                  // ),
-                },
-                {
-                  title: "分值",
-                  dataIndex: "score",
-                  align: "center",
-                  width: "130px",
-                  // formItemProps: () => {
-                  //   return {
-                  //     rules: [{ required: true, message: "此项为必填项" }],
-                  //   };
-                  // },
-                  // renderFormItem: () => (
-                  //   <InputNumber
-                  //     variant="borderless"
-                  //     size="small"
-                  //     precision={0}
-                  //     placeholder="请填入正整数"
-                  //     className="w100"
-                  //   />
-                  // ),
-                },
-                // {
-                //   title: "操作",
-                //   align: "center",
-                //   valueType: "option",
-                //   render: (node, row) => {
-                //     return (
-                //       <DageTableActions
-                //         showEdit={false}
-                //         onDelete={handleDelete.bind(undefined, row)}
-                //       />
-                //     );
-                //   },
-                // },
-              ]}
+              columns={
+                isFixed
+                  ? [
+                      {
+                        title: "指标级别",
+                        dataIndex: "level",
+                        align: "center",
+                        editable: false,
+                      },
+                      {
+                        title: "指标名称",
+                        dataIndex: "name",
+                        align: "center",
+                        editable: false,
+                      },
+                      {
+                        title: "填报方式",
+                        dataIndex: "fill",
+                        align: "center",
+                        width: "230px",
+                        // renderFormItem: () => (
+                        //   <Radio.Group>
+                        //     <Radio value="point">手动填报</Radio>
+                        //     <Radio value="api">
+                        //       API{" "}
+                        //       <Tooltip title="以 type,start_date,end_date请求API数据。当API获取数据失败时,将自动转为手动填报">
+                        //         <Tag bordered={false} color="warning">
+                        //           注
+                        //         </Tag>
+                        //       </Tooltip>
+                        //     </Radio>
+                        //   </Radio.Group>
+                        // ),
+                      },
+                      {
+                        title: "分值",
+                        dataIndex: "score",
+                        align: "center",
+                        width: "130px",
+                      },
+                    ]
+                  : [
+                      {
+                        title: "指标级别",
+                        dataIndex: "level",
+                        align: "center",
+                        editable: false,
+                      },
+                      {
+                        title: "指标名称",
+                        dataIndex: "name",
+                        align: "center",
+                        editable: false,
+                      },
+                      {
+                        title: "指标权重",
+                        dataIndex: "weight",
+                        align: "center",
+                        width: "130px",
+                        render: (_, row) =>
+                          row.weight <= 0 ? "/" : row.weight,
+                        formItemProps: () => {
+                          return {
+                            rules: [
+                              { required: true, message: "此项为必填项" },
+                            ],
+                          };
+                        },
+                        renderFormItem: () => (
+                          <InputNumber
+                            variant="borderless"
+                            size="small"
+                            min={1}
+                            max={100}
+                            controls={false}
+                            precision={0}
+                            placeholder="请输入1~100的数字"
+                            className="w100"
+                            addonAfter="%"
+                          />
+                        ),
+                      },
+                      {
+                        title: "操作",
+                        valueType: "option",
+                        render: (_, row) => [
+                          <a
+                            key="edit"
+                            onClick={() => {
+                              actionRef.current?.startEditable(row.id);
+                            }}
+                          >
+                            编辑
+                          </a>,
+                        ],
+                      },
+                    ]
+              }
               headerTitle={
                 <Space>
                   <Button
@@ -237,13 +271,11 @@ const SettingIndexPage: FC = () => {
               ]}
               editable={{
                 type: "multiple",
-                editableKeys,
                 actionRender: (row, config, defaultDoms) => {
-                  return [defaultDoms.delete];
+                  return [defaultDoms.save, defaultDoms.cancel];
                 },
-                onValuesChange: (record, recordList) => {
-                  // form.setFieldValue("list", recordList);
-                  setEditableKeys(recordList.map((i) => i.id));
+                onSave: async (id, row) => {
+                  await saveManageAssOperationWeightApi(row.id, row.weight);
                 },
               }}
             />

+ 7 - 2
src/pages/Management/Index/SettingRole/index.tsx

@@ -54,7 +54,7 @@ const SettingRole: FC = () => {
       setGroupLoading(true);
       const data = await getManageRoleGroupListApi(params.id as string);
       // fake array
-      setGroupList([data]);
+      setGroupList(Array.isArray(data) ? data : Boolean(data) ? [data] : []);
     } finally {
       setGroupLoading(false);
     }
@@ -199,6 +199,7 @@ const SettingRole: FC = () => {
         />
       </Pane>
 
+      {/* 设置责任部门 */}
       <AddDeptModal
         item={checkedItem}
         assessId={params.id as string}
@@ -210,15 +211,17 @@ const SettingRole: FC = () => {
         }}
       />
 
+      {/* 设置评定组 */}
       <AddGroupModal
         assessId={params.id as string}
         open={addGroupVisible}
-        onOk={getDeptList}
+        onOk={getGroupList}
         onCancel={() => {
           setAddGroupVisible(false);
         }}
       />
 
+      {/* 分配资料 */}
       <AllocationOfDataModal
         item={checkedItem}
         deptList={deptList}
@@ -231,11 +234,13 @@ const SettingRole: FC = () => {
         }}
       />
 
+      {/* 分配指标 */}
       <AllocationOfIndexModal
         type={params.type as ASS_INDEX_TYPE}
         item={checkedItem}
         assessId={params.id as string}
         open={allocationOfIndexVisible}
+        deptList={deptList}
         onOk={getDeptList}
         onCancel={() => {
           setAllocationOfIndexVisible(false);

+ 13 - 0
src/pages/Management/Index/components/AllocationOfIndexDataModal/index.module.scss

@@ -0,0 +1,13 @@
+.pane {
+  padding: 15px;
+  background: #fafafa;
+
+  &:first-child {
+    margin-bottom: 15px;
+  }
+  p {
+    margin-bottom: 10px;
+    font-size: 18px;
+    font-weight: bold;
+  }
+}

+ 167 - 0
src/pages/Management/Index/components/AllocationOfIndexDataModal/index.tsx

@@ -0,0 +1,167 @@
+import { Modal, ModalProps, Select, Tag } from "antd";
+import { FC, useEffect, useMemo, useRef, useState } from "react";
+import style from "./index.module.scss";
+import {
+  EditableProTable,
+  ProForm,
+  ProFormInstance,
+} from "@ant-design/pro-components";
+import {
+  IAssIndexDetail,
+  IManageDeptItem,
+  IManageDeptMaterialItem,
+  MaterialType,
+  YES_OR_NO,
+} from "@/types";
+import { saveManageDeptAllocationOfDataApi } from "@/api";
+
+export interface AllocationOfIndexDataModalProps
+  extends Omit<ModalProps, "onOk"> {
+  indexList: IAssIndexDetail[];
+  deptList: IManageDeptItem[];
+  onCancel?: () => void;
+  onOk?: () => void;
+}
+
+export const AllocationOfIndexDataModal: FC<
+  AllocationOfIndexDataModalProps
+> = ({ open, indexList, deptList, onOk, onCancel, ...rest }) => {
+  const formRef = useRef<ProFormInstance<any>>();
+  const [loading, setLoading] = useState(false);
+  const list = useMemo(
+    () => indexList.filter((item) => Boolean(item.materials.length)),
+    [indexList]
+  );
+
+  useEffect(() => {
+    if (!list.length) return;
+
+    list.forEach((item, index) => {
+      formRef.current?.setFieldValue("table" + index, item.materials);
+    });
+  }, [list]);
+
+  const handleCancel = () => {
+    onCancel?.();
+    formRef.current?.resetFields();
+  };
+
+  const handleConfirm = async () => {
+    if (!(await formRef.current?.validateFields())) return;
+
+    const vals = formRef.current?.getFieldsValue();
+    const temp: Record<number, number> = {};
+
+    Object.keys(vals).forEach((key) => {
+      const arr = vals[key];
+      arr.forEach((i: IManageDeptMaterialItem) => {
+        temp[i.id] = i.deptId as number;
+      });
+    });
+
+    try {
+      setLoading(true);
+      await saveManageDeptAllocationOfDataApi(temp);
+      onOk?.();
+      handleCancel();
+    } finally {
+      setLoading(false);
+    }
+  };
+
+  return (
+    <Modal
+      title="分配指标"
+      okText="提交"
+      cancelText="取消"
+      open={open}
+      width={960}
+      okButtonProps={{
+        disabled: loading,
+      }}
+      maskClosable={false}
+      onOk={handleConfirm}
+      onCancel={handleCancel}
+      {...rest}
+    >
+      <p style={{ marginBottom: 15 }}>
+        以下指标由多个部门负责,需为关联资料上传指定唯一部门
+      </p>
+
+      <ProForm
+        labelCol={{ span: 4, offset: 2 }}
+        formRef={formRef}
+        submitter={false}
+      >
+        {list.map((item, index) => (
+          <div className={style.pane} key={item.id}>
+            <p>{item.name}</p>
+
+            <EditableProTable<MaterialType>
+              name={`table${index}`}
+              rowKey="id"
+              recordCreatorProps={{
+                style: {
+                  display: "none",
+                },
+                // @ts-ignore
+                record: () => ({}),
+              }}
+              columns={[
+                {
+                  title: "标题",
+                  dataIndex: "name",
+                  editable: false,
+                },
+                {
+                  title: "是否必填",
+                  editable: false,
+                  render: (node, item) => {
+                    return item.isUpload === YES_OR_NO.YES ? "是" : "否";
+                  },
+                },
+                {
+                  title: "文件类型",
+                  editable: false,
+                  width: 300,
+                  render: (node, item) => {
+                    const arr = item.suffix?.split(",") || [];
+                    return (
+                      <>
+                        {arr.map((i) => (
+                          <Tag key={i}>{i}</Tag>
+                        ))}
+                      </>
+                    );
+                  },
+                },
+                {
+                  title: "责任部门",
+                  dataIndex: "deptId",
+                  width: 260,
+                  formItemProps: () => {
+                    return {
+                      rules: [{ required: true, message: "此项为必填项" }],
+                    };
+                  },
+                  renderFormItem: () => (
+                    <Select
+                      allowClear
+                      options={deptList}
+                      fieldNames={{ value: "id", label: "name" }}
+                      placeholder="请选择"
+                    />
+                  ),
+                },
+              ]}
+              editable={{
+                type: "multiple",
+                editableKeys: item.materials.map((i) => i.id),
+              }}
+            />
+          </div>
+        ))}
+      </ProForm>
+    </Modal>
+  );
+};

+ 124 - 72
src/pages/Management/Index/components/AllocationOfIndexModal/index.tsx

@@ -1,20 +1,24 @@
 import { FC, Key, useEffect, useState } from "react";
-import { Checkbox, Form, Modal, ModalProps, Tree } from "antd";
+import { Checkbox, Empty, Form, Modal, ModalProps, Tree } from "antd";
 import {
   IManageDeptAllocationOfIndexItem,
   ASS_INDEX_TYPE,
   IManageDeptItem,
+  IAssIndexDetail,
 } from "@/types";
 import { DageLoading } from "@dage/pc-components";
 import {
+  checkManageIndexApi,
   getManageDeptAllocationOfIndexListApi,
   saveManageDeptAllocationOfIndexApi,
 } from "@/api";
+import { AllocationOfIndexDataModal } from "../AllocationOfIndexDataModal";
 
 export interface AllocationOfIndexModalProps extends Omit<ModalProps, "onOk"> {
   type: ASS_INDEX_TYPE;
   assessId: number | string;
   item?: null | IManageDeptItem;
+  deptList: IManageDeptItem[];
   onCancel?: () => void;
   onOk?: () => void;
 }
@@ -24,6 +28,7 @@ export const AllocationOfIndexModal: FC<AllocationOfIndexModalProps> = ({
   type,
   assessId,
   item,
+  deptList,
   onOk,
   onCancel,
   ...rest
@@ -35,32 +40,53 @@ export const AllocationOfIndexModal: FC<AllocationOfIndexModalProps> = ({
   );
   const [loading, setLoading] = useState(false);
   const assign = Form.useWatch("assign", form);
+  const [indexList, setIndexList] = useState<IAssIndexDetail[]>([]);
+  const [allocationOfIndexDataVisible, setAllocationOfIndexDataVisible] =
+    useState(false);
 
-  const transformTreeData = (data: IManageDeptAllocationOfIndexItem[]) => {
-    return data.map((item) => {
-      const newNode: any = {
-        ...item,
-        disableCheckbox: item.perm,
-      };
+  const getDefaultCheckedKeys = (data: IManageDeptAllocationOfIndexItem[]) => {
+    let keys: number[] = [];
+
+    data.forEach((item) => {
+      if (item.perm) keys.push(item.id);
+
+      if (item.children && item.children.length > 0) {
+        keys = keys.concat(getDefaultCheckedKeys(item.children));
+      }
+    });
+
+    return keys;
+  };
+
+  const getUnAllocationTree = (data: IManageDeptAllocationOfIndexItem[]) => {
+    return data.filter((item) => {
+      if (item.perm) return false;
 
       if (item.children && item.children.length > 0) {
-        newNode.children = transformTreeData(item.children);
+        item.children = getUnAllocationTree(item.children);
       }
 
-      return newNode;
+      return true;
     });
   };
 
   const getAssIndexTree = async (assign?: number) => {
     try {
       setLoading(true);
+      setCheckedKeys([]);
+      setTreeData([]);
       const data = await getManageDeptAllocationOfIndexListApi(
         assessId,
         item?.id as number,
         type,
         assign
       );
-      setTreeData(assign ? transformTreeData(data) : data);
+      if (!assign) {
+        setTreeData(data);
+        setCheckedKeys(getDefaultCheckedKeys(data));
+      } else {
+        setTreeData(getUnAllocationTree(data));
+      }
     } finally {
       setLoading(false);
     }
@@ -77,83 +103,109 @@ export const AllocationOfIndexModal: FC<AllocationOfIndexModalProps> = ({
   };
 
   const handleSubmit = async (values: any) => {
-    await saveManageDeptAllocationOfIndexApi({
+    const data = await checkManageIndexApi({
       assessId,
-      deptId: item?.id as number,
-      normIds: values.onlyChildKeys,
+      normIds: values.checkedKeys.join(","),
     });
-    onOk?.();
+
+    if (!data.length) {
+      await saveManageDeptAllocationOfIndexApi({
+        assessId,
+        deptId: item?.id as number,
+        normIds: values.checkedKeys,
+      });
+      onOk?.();
+    } else {
+      setIndexList(data);
+      setAllocationOfIndexDataVisible(true);
+    }
+
     handleCancel();
   };
 
   const handleAfterOpenChange = (open: boolean) => {
-    if (open && !treeData.length) getAssIndexTree();
+    if (open) getAssIndexTree();
   };
 
   useEffect(() => {
     if (!Array.isArray(assign)) return;
     getAssIndexTree(assign[0]);
-    form.resetFields(["onlyChildKeys"]);
+    form.resetFields(["checkedKeys"]);
   }, [assign]);
 
   return (
-    <Modal
-      title="分配指标"
-      okText="提交"
-      cancelText="取消"
-      open={open}
-      width={640}
-      maskClosable={false}
-      okButtonProps={{
-        disabled: false,
-      }}
-      onOk={handleConfirm}
-      onCancel={handleCancel}
-      {...rest}
-      afterOpenChange={handleAfterOpenChange}
-    >
-      <Form
-        labelCol={{ span: 4, offset: 2 }}
-        form={form}
-        onFinish={handleSubmit}
+    <>
+      <Modal
+        title="分配指标"
+        okText="提交"
+        cancelText="取消"
+        open={open}
+        width={640}
+        maskClosable={false}
+        okButtonProps={{
+          disabled: false,
+        }}
+        onOk={handleConfirm}
+        onCancel={handleCancel}
+        {...rest}
+        afterOpenChange={handleAfterOpenChange}
       >
-        <Form.Item name="assign" label=" " colon={false}>
-          <Checkbox.Group
-            options={[
-              {
-                label: "仅查看尚未分配的指标",
-                value: 1,
-              },
-            ]}
-          />
-        </Form.Item>
-        <Form.Item
-          label="采用指标"
-          name="onlyChildKeys"
-          required
-          rules={[{ required: true, message: "请选择指标" }]}
+        <Form
+          labelCol={{ span: 4, offset: 2 }}
+          form={form}
+          onFinish={handleSubmit}
         >
-          <Tree
-            checkable
-            defaultExpandAll
-            fieldNames={{ title: "name", key: "id" }}
-            checkedKeys={_checkedKeys}
-            treeData={treeData}
-            onCheck={(checkedKeys, { checkedNodes }) => {
-              const onlyChildKeys = (checkedKeys as Key[]).filter((i) => {
-                const node = checkedNodes.find((n) => n.id === i);
-                return !node?.children.length;
-              });
-              setCheckedKeys(onlyChildKeys);
-              form.setFieldsValue({
-                onlyChildKeys,
-              });
-            }}
-          />
-        </Form.Item>
-      </Form>
-
-      {loading && <DageLoading />}
-    </Modal>
+          <Form.Item name="assign" label=" " colon={false}>
+            <Checkbox.Group
+              options={[
+                {
+                  label: "仅查看尚未分配的指标",
+                  value: 1,
+                },
+              ]}
+            />
+          </Form.Item>
+          <Form.Item
+            label="采用指标"
+            name="checkedKeys"
+            required
+            rules={[{ required: true, message: "请选择指标" }]}
+          >
+            <div>
+              {!treeData.length && !loading && (
+                <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} />
+              )}
+
+              <Tree
+                checkable
+                defaultExpandAll
+                fieldNames={{ title: "name", key: "id" }}
+                checkedKeys={_checkedKeys}
+                treeData={treeData}
+                onCheck={(keys) => {
+                  setCheckedKeys(keys as Key[]);
+                  form.setFieldsValue({
+                    checkedKeys: keys,
+                  });
+                }}
+              />
+            </div>
+          </Form.Item>
+        </Form>
+
+        {loading && <DageLoading />}
+      </Modal>
+
+      <AllocationOfIndexDataModal
+        open={allocationOfIndexDataVisible}
+        indexList={indexList}
+        deptList={deptList}
+        onOk={() => {
+          onOk?.();
+          setAllocationOfIndexDataVisible(false);
+        }}
+        onCancel={() => setAllocationOfIndexDataVisible(false)}
+      />
+    </>
   );
 };

+ 46 - 0
src/pages/Management/Index/components/WarningModal/index.tsx

@@ -0,0 +1,46 @@
+import { WARNING_TYPE } from "@/types";
+import { Modal, ModalProps } from "antd";
+import { FC } from "react";
+
+export interface WarningModalProps extends Omit<ModalProps, "onOk"> {
+  type: WARNING_TYPE;
+  onCancel?: () => void;
+  onOk?: () => void;
+}
+
+const WARNING = [
+  "为确保考核公正,发布后,不可编辑考核信息及指标。",
+  "终止考核后,将无法继续填报和评定",
+];
+
+export const WarningModal: FC<WarningModalProps> = ({
+  type,
+  open,
+  onOk,
+  onCancel,
+  ...rest
+}) => {
+  const handleCancel = () => {
+    onCancel?.();
+  };
+
+  const handleConfirm = () => {
+    onOk?.();
+  };
+
+  return (
+    <Modal
+      title="提示"
+      okText="确定"
+      cancelText="取消"
+      maskClosable={false}
+      open={open}
+      width={400}
+      onOk={handleConfirm}
+      onCancel={handleCancel}
+      {...rest}
+    >
+      <p>{WARNING[type]}</p>
+    </Modal>
+  );
+};

+ 1 - 0
src/pages/Management/Index/components/index.ts

@@ -2,3 +2,4 @@ export * from "./AllocationOfDataModal";
 export * from "./AllocationOfIndexModal";
 export * from "./AddGroupModal";
 export * from "./AddDeptModal";
+export * from "./WarningModal";

+ 37 - 14
src/pages/Management/Index/index.tsx

@@ -1,5 +1,5 @@
 import classNames from "classnames";
-import { useCallback, useEffect, useMemo, useState } from "react";
+import { useCallback, useEffect, useMemo, useRef, useState } from "react";
 import { Button, Dropdown, Form, Input, Select, Table } from "antd";
 import { PlusOutlined } from "@ant-design/icons";
 import { useNavigate } from "react-router-dom";
@@ -17,7 +17,13 @@ import {
   publishManageIndexApi,
 } from "@/api";
 import { debounce } from "lodash";
-import { ASS_INDEX_TYPE, IManageIndexDetail, PUBLISH_ENUM } from "@/types";
+import {
+  ASS_INDEX_TYPE,
+  IManageIndexDetail,
+  PUBLISH_ENUM,
+  WARNING_TYPE,
+} from "@/types";
+import { WarningModal } from "./components";
 
 const DEFAULT_PARAMS = {
   pageNum: 1,
@@ -36,6 +42,9 @@ const ManagementIndexPage = () => {
   });
   const [total, setTotal] = useState(0);
   const [list, setList] = useState<IManageIndexDetail[]>([]);
+  const [warningVisible, setWarningVisible] = useState(false);
+  const [warningType, setWarningType] = useState(WARNING_TYPE.PUBLISH);
+  const checkedItem = useRef<IManageIndexDetail | null>(null);
 
   const debounceSearch = useMemo(
     () =>
@@ -76,6 +85,13 @@ const ManagementIndexPage = () => {
     getList();
   };
 
+  const handleWarningConfirm = () => {
+    if (!checkedItem.current) return;
+
+    handlePublish(checkedItem.current.id, warningType === WARNING_TYPE.STOP);
+    setWarningVisible(false);
+  };
+
   useEffect(() => {
     getList();
   }, []);
@@ -219,7 +235,6 @@ const ManagementIndexPage = () => {
               align: "center",
               fixed: "right",
               render: (item: IManageIndexDetail) => {
-                const isEnded = item.status === PUBLISH_ENUM.ENDED;
                 const isPending = item.status === PUBLISH_ENUM.PENDING;
                 const showStopBtn = [
                   PUBLISH_ENUM.EVALUATED,
@@ -228,6 +243,7 @@ const ManagementIndexPage = () => {
 
                 return (
                   <DageTableActions
+                    showEdit={item.status === PUBLISH_ENUM.PENDING}
                     onEdit={() =>
                       navigate(`/management/index/edit/${item.type}/${item.id}`)
                     }
@@ -248,11 +264,11 @@ const ManagementIndexPage = () => {
                             variant="text"
                             color="primary"
                             className={style.button}
-                            onClick={handlePublish.bind(
-                              undefined,
-                              item.id,
-                              false
-                            )}
+                            onClick={() => {
+                              checkedItem.current = item;
+                              setWarningType(WARNING_TYPE.PUBLISH);
+                              setWarningVisible(true);
+                            }}
                           >
                             发布
                           </Button>
@@ -262,16 +278,16 @@ const ManagementIndexPage = () => {
                             variant="text"
                             color="danger"
                             className={style.button}
-                            onClick={handlePublish.bind(
-                              undefined,
-                              item.id,
-                              true
-                            )}
+                            onClick={() => {
+                              checkedItem.current = item;
+                              setWarningType(WARNING_TYPE.STOP);
+                              setWarningVisible(true);
+                            }}
                           >
                             终止
                           </Button>
                         )}
-                        {!isEnded && (
+                        {isPending && (
                           <Dropdown
                             placement="bottom"
                             arrow
@@ -331,6 +347,13 @@ const ManagementIndexPage = () => {
           }}
         />
       </div>
+
+      <WarningModal
+        type={warningType}
+        open={warningVisible}
+        onOk={handleWarningConfirm}
+        onCancel={() => setWarningVisible(false)}
+      />
     </PageContainer>
   );
 };

+ 1 - 1
src/router/index.tsx

@@ -133,7 +133,7 @@ export const DEFAULT_MENU: DageRouteItem[] = [
             meta: {
               custom: true,
             },
-            path: "/management/form/detail/:id",
+            path: "/management/form/detail/:id/:accessId",
             title: "考核详情",
             Component: React.lazy(() => import("../pages/AssessmentDetail")),
           },

+ 5 - 7
src/types/assessment.ts

@@ -1,4 +1,4 @@
-import { YES_OR_NO } from ".";
+import { MaterialType, YES_OR_NO } from ".";
 
 export enum ASS_INDEX_TYPE {
   /**
@@ -16,6 +16,7 @@ export type AssIndexTreeItemType = {
   name: string;
   parentId: number;
   level: number;
+  isPoint: YES_OR_NO;
   children: AssIndexTreeItemType[];
   disabled?: boolean;
 };
@@ -33,15 +34,12 @@ export interface IAssIndexDetail {
   /** 告警阈值 */
   isWarn: YES_OR_NO;
   jsonWarn: string;
-  materials: {
-    name: string;
-    id: number;
-    fileName: string;
-    isUpload: YES_OR_NO;
-  }[];
+  materials: MaterialType[];
   sort: number;
   updateTime: string;
   creatorName: string;
+  level: number;
+  weight: number;
   gists?: IAssInspectionItem[];
 }
 

+ 20 - 1
src/types/index.ts

@@ -1,5 +1,8 @@
 import { DageFileResponseType } from "@dage/pc-components";
 
+/** norm:指标 | assess:考核 | fill:填报 */
+export type ModuleType = "norm" | "assess" | "fill-norm" | "fill-assess";
+
 export interface LoginRequest {
   userName: string;
   passWord: string | string[];
@@ -36,11 +39,14 @@ export interface IFileTemplateForm {
 }
 export interface IFileTemplateFormParams
   extends Omit<IFileTemplateForm, "file" | "suffix"> {
+  id?: number;
   fileName?: string;
   filePath?: string;
   suffix?: string;
   level: number;
-  module: "norm" | "assess";
+  module: ModuleType;
+  moduleId?: number;
+  parentId?: number;
 }
 export interface IFileTemplateFormResponse
   extends Required<IFileTemplateFormParams> {
@@ -52,6 +58,19 @@ export enum YES_OR_NO {
   NO = 0,
 }
 
+export type MaterialType = {
+  id: number;
+  name: string;
+  fileName: string;
+  filePath: string;
+  suffix: string;
+  level: number;
+  isUpload: YES_OR_NO;
+  module: ModuleType;
+  moduleId: number;
+  parentId: number | null;
+};
+
 export * from "./log";
 export * from "./user";
 export * from "./assessment";

+ 47 - 12
src/types/management.ts

@@ -1,6 +1,11 @@
 import { PaginationParams } from "@dage/service";
-import { IFileTemplateFormParams, YES_OR_NO } from ".";
-import { ASS_INDEX_TYPE } from "./assessment";
+import {
+  IFileTemplateFormParams,
+  MaterialType,
+  ModuleType,
+  YES_OR_NO,
+} from ".";
+import { ASS_INDEX_TYPE, IAssInspectionItem } from "./assessment";
 
 /**
  * 发布状态
@@ -26,16 +31,7 @@ export interface IManageIndexDetail {
   createTime: string;
   jsonAdd: string;
   jsonSub: string;
-  materials:
-    | {
-        id: number;
-        name: string;
-        fileName: string;
-        filePath: string;
-        suffix: string;
-        isUpload: YES_OR_NO;
-      }[]
-    | null;
+  materials: MaterialType[] | null;
 }
 
 export interface IManageAssessmentIndex {
@@ -72,6 +68,7 @@ export interface IManageFormListParams extends PaginationParams {
 
 export interface IManageFormItem {
   id: number;
+  accessId: number;
   name: string;
   type: ASS_INDEX_TYPE;
   remark: string;
@@ -84,6 +81,13 @@ export interface IManageFormItem {
 
 export interface IManageFormDetail extends Omit<IManageIndexDetail, "status"> {
   deptStatus: DEPT_STATUS_ENUM;
+  publishStatus: PUBLISH_ENUM;
+  /** 是否开启自评 */
+  isSelf: YES_OR_NO;
+  deptName: string;
+  accessId: number;
+  /** 是否为部门主管 */
+  leaderUserId: YES_OR_NO;
 }
 
 export interface IManageDeptItem {
@@ -130,4 +134,35 @@ export interface IManageAssOperationResponse {
     two: number;
     three: number;
   };
+  list: ({
+    level: number;
+    weight: number;
+  } & IAssInspectionItem)[];
+}
+
+export interface ISaveManageFileParams {
+  fileName: string;
+  filePath: string;
+  level: number;
+  module: ModuleType;
+  moduleId?: number;
+  name: string;
+  suffix: string;
+  parentId?: number;
+}
+
+export interface IManageNormItem {
+  id: number;
+  normId: number;
+  name: string;
+  remark: string;
+  score: number | null;
+  selfScore: number | null;
+  isPoint: YES_OR_NO;
+  deptName: string;
+}
+
+export enum WARNING_TYPE {
+  PUBLISH = 0,
+  STOP = 1,
 }

+ 13 - 0
src/utils/index.ts

@@ -2,6 +2,8 @@ import { removeTokenInfo } from "@dage/pc-components";
 import { logoutApi } from "@/api";
 import { Key } from "react";
 import { AssIndexTreeItemType } from "@/types";
+import { RcFile } from "antd/es/upload";
+import { message } from "antd";
 
 export const logout = async () => {
   await logoutApi();
@@ -52,3 +54,14 @@ export const downloadFile = async (url: string, name: string) => {
     console.error("下载失败:", error);
   }
 };
+
+export const beforeUpload = (suffix: string, file: RcFile) => {
+  const arr = suffix.split(",");
+
+  if (!arr.length) return true;
+  const result = arr.findIndex((i) => file.name.indexOf(i) > -1) > -1;
+  if (!result) {
+    message.error("选择的文件类型不正确!");
+  }
+  return result;
+};