|
|
@@ -1,4 +1,4 @@
|
|
|
-import { Button, Form, Radio, Table, Tag } from "antd";
|
|
|
+import { Button, Empty, Form, Radio, Table, Tag } from "antd";
|
|
|
import { FC, useEffect, useMemo, useRef, useState } from "react";
|
|
|
import classNames from "classnames";
|
|
|
import {
|
|
|
@@ -19,7 +19,7 @@ import {
|
|
|
REVIEW_MATERIAL_TYPE,
|
|
|
YES_OR_NO,
|
|
|
} from "@/types";
|
|
|
-import { downloadFile, beforeUpload } from "@/utils";
|
|
|
+import { downloadFile, beforeUpload, claimPendingUploadFiles, hasUploadingFiles, getUploadFileSaveKey } from "@/utils";
|
|
|
import { getBaseURL } from "@dage/service";
|
|
|
import {
|
|
|
additionalEvaOpinionApi,
|
|
|
@@ -34,7 +34,7 @@ import { EvaluationFormModal } from "../EvaluationFormModal";
|
|
|
import { ColumnsType } from "antd/es/table";
|
|
|
import { useSelector } from "react-redux";
|
|
|
import { RootState } from "@/store";
|
|
|
-import { REVIEW_MATERIAL_STATUS_MAP } from "@/constants";
|
|
|
+import { DEPT_REVIEW_STATUS_MAP } from "@/constants";
|
|
|
|
|
|
export interface OverallAssessmentProps {
|
|
|
detail: IManageIndexDetail | IManageFormDetail | null;
|
|
|
@@ -54,7 +54,7 @@ export const OverallAssessment: FC<OverallAssessmentProps> = ({
|
|
|
refreshDetail,
|
|
|
}) => {
|
|
|
const { userInfo } = useSelector<RootState, RootState["base"]>(
|
|
|
- (state) => state.base
|
|
|
+ (state) => state.base,
|
|
|
);
|
|
|
const baseUrl = getBaseURL();
|
|
|
// 是否能够上传附件
|
|
|
@@ -91,6 +91,21 @@ export const OverallAssessment: FC<OverallAssessmentProps> = ({
|
|
|
const [uploadedFileMap, setUploadedFileMap] = useState<
|
|
|
Record<number, MaterialType[]>
|
|
|
>({});
|
|
|
+ // 记录已提交保存的文件 key,避免 Upload 内部 fileList 累积导致重复提交
|
|
|
+ const savedFileKeysRef = useRef<Set<string>>(new Set());
|
|
|
+ const [uploadKeys, setUploadKeys] = useState<Record<number, number>>({});
|
|
|
+
|
|
|
+ const resetUpload = (parentId: number) => {
|
|
|
+ setUploadKeys((prev) => ({
|
|
|
+ ...prev,
|
|
|
+ [parentId]: (prev[parentId] || 0) + 1,
|
|
|
+ }));
|
|
|
+ };
|
|
|
+ // 资料上传列表
|
|
|
+ const materials = useMemo(
|
|
|
+ () => detail?.materials?.filter((i) => i.module === "assess") || [],
|
|
|
+ [detail],
|
|
|
+ );
|
|
|
const fileColumns = useMemo(() => {
|
|
|
const stack: ColumnsType<MaterialType> = [
|
|
|
{
|
|
|
@@ -132,7 +147,7 @@ export const OverallAssessment: FC<OverallAssessmentProps> = ({
|
|
|
: "error"
|
|
|
}
|
|
|
>
|
|
|
- {REVIEW_MATERIAL_STATUS_MAP[item.status]}
|
|
|
+ {DEPT_REVIEW_STATUS_MAP[item.status]}
|
|
|
</Tag>
|
|
|
) : (
|
|
|
<Radio.Group
|
|
|
@@ -179,7 +194,7 @@ export const OverallAssessment: FC<OverallAssessmentProps> = ({
|
|
|
填写
|
|
|
</Button>
|
|
|
),
|
|
|
- }
|
|
|
+ },
|
|
|
);
|
|
|
} else {
|
|
|
stack.splice(2, 0, {
|
|
|
@@ -202,7 +217,7 @@ export const OverallAssessment: FC<OverallAssessmentProps> = ({
|
|
|
onClick={() => {
|
|
|
downloadFile(
|
|
|
baseUrl + process.env.REACT_APP_IMG_PUBLIC + val.filePath,
|
|
|
- val.fileName
|
|
|
+ val.fileName,
|
|
|
);
|
|
|
}}
|
|
|
>
|
|
|
@@ -211,7 +226,7 @@ export const OverallAssessment: FC<OverallAssessmentProps> = ({
|
|
|
}
|
|
|
showDelete={isReportDetail && canUpload}
|
|
|
showEdit={false}
|
|
|
- onDelete={handleDeleteFile.bind(undefined, val.id)}
|
|
|
+ onDelete={handleDeleteFile.bind(undefined, val.id, val.parentId!)}
|
|
|
/>
|
|
|
),
|
|
|
});
|
|
|
@@ -254,12 +269,12 @@ export const OverallAssessment: FC<OverallAssessmentProps> = ({
|
|
|
try {
|
|
|
setLoading(true);
|
|
|
const data = await getFileListApi(
|
|
|
- isIndexDetail ? detail!.id : (detail as IManageFormDetail).accessId,
|
|
|
+ (detail as IManageFormDetail).assessId,
|
|
|
"assess",
|
|
|
- detail!.id
|
|
|
+ detail!.id,
|
|
|
);
|
|
|
const temp: typeof uploadedFileMap = {};
|
|
|
- data.forEach((item) => {
|
|
|
+ data.fill.forEach((item) => {
|
|
|
if (!item.parentId) return;
|
|
|
if (Array.isArray(temp[item.parentId])) temp[item.parentId].push(item);
|
|
|
else temp[item.parentId] = [item];
|
|
|
@@ -272,37 +287,48 @@ export const OverallAssessment: FC<OverallAssessmentProps> = ({
|
|
|
|
|
|
const saveManageFile = async (
|
|
|
item: MaterialType,
|
|
|
- list: DageFileResponseType[]
|
|
|
+ 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];
|
|
|
+ const pendingFiles = claimPendingUploadFiles(
|
|
|
+ item.id,
|
|
|
+ list,
|
|
|
+ savedFileKeysRef.current,
|
|
|
+ );
|
|
|
|
|
|
- if (!file.response) return;
|
|
|
+ if (!pendingFiles.length) 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?.id,
|
|
|
- deptId: detail?.id,
|
|
|
- assessId: (detail as IManageFormDetail).accessId,
|
|
|
+ try {
|
|
|
+ for (const file of pendingFiles) {
|
|
|
+ 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?.id,
|
|
|
+ deptId: detail?.id,
|
|
|
+ assessId: (detail as IManageFormDetail).assessId,
|
|
|
+ });
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ pendingFiles.forEach((file) => {
|
|
|
+ savedFileKeysRef.current.delete(getUploadFileSaveKey(item.id, file));
|
|
|
});
|
|
|
- // @ts-ignore
|
|
|
- file.uploaded = true;
|
|
|
+ throw error;
|
|
|
}
|
|
|
|
|
|
- getList();
|
|
|
+ await getList();
|
|
|
+
|
|
|
+ if (!hasUploadingFiles(list)) {
|
|
|
+ resetUpload(item.id);
|
|
|
+ }
|
|
|
};
|
|
|
|
|
|
- const handleDeleteFile = async (id: number) => {
|
|
|
- await deleteFileApi(id);
|
|
|
+ const handleDeleteFile = async (fileId: number, parentId: number) => {
|
|
|
+ await deleteFileApi(fileId);
|
|
|
+ resetUpload(parentId);
|
|
|
getList();
|
|
|
};
|
|
|
|
|
|
@@ -311,7 +337,7 @@ export const OverallAssessment: FC<OverallAssessmentProps> = ({
|
|
|
|
|
|
if (curEvaluationData.current.type === "assessment") {
|
|
|
await assessmentEvaOpinionApi({
|
|
|
- deptId: curEvaluationData.current.id,
|
|
|
+ assessId: curEvaluationData.current.id,
|
|
|
...val,
|
|
|
});
|
|
|
} else {
|
|
|
@@ -338,7 +364,7 @@ export const OverallAssessment: FC<OverallAssessmentProps> = ({
|
|
|
assessor: userInfo?.user.realName,
|
|
|
});
|
|
|
await additionalEvaOpinionApi({
|
|
|
- assessId: (detail as IManageFormDetail).accessId,
|
|
|
+ assessId: (detail as IManageFormDetail).assessId,
|
|
|
deptId: detail!.id,
|
|
|
opinionJson: JSON.stringify(temp),
|
|
|
type: checkedAddItem.current.id.indexOf("bonus") > -1 ? "add" : "sub",
|
|
|
@@ -348,6 +374,8 @@ export const OverallAssessment: FC<OverallAssessmentProps> = ({
|
|
|
|
|
|
useEffect(() => {
|
|
|
if (!detail) return;
|
|
|
+ savedFileKeysRef.current.clear();
|
|
|
+ setUploadKeys({});
|
|
|
getList();
|
|
|
}, [detail]);
|
|
|
|
|
|
@@ -392,20 +420,20 @@ export const OverallAssessment: FC<OverallAssessmentProps> = ({
|
|
|
title: "部门考核状态",
|
|
|
align: "center",
|
|
|
// @ts-ignore
|
|
|
- render: (val) => REVIEW_MATERIAL_STATUS_MAP[val.status],
|
|
|
+ render: (val) => DEPT_REVIEW_STATUS_MAP[val.status],
|
|
|
},
|
|
|
]}
|
|
|
/>
|
|
|
</Form.Item>
|
|
|
|
|
|
<Form.Item label="评定意见">
|
|
|
- {isEvalutionDetail ? (
|
|
|
+ {isEvalutionDetail && (
|
|
|
<Button
|
|
|
type="primary"
|
|
|
onClick={() => {
|
|
|
curEvaluationData.current = {
|
|
|
type: "assessment",
|
|
|
- id: detail!.id,
|
|
|
+ id: (detail as IManageFormDetail)!.assessId,
|
|
|
};
|
|
|
setInitEvaluationContent(detail!.opinion);
|
|
|
setEvaluationVisible(true);
|
|
|
@@ -413,38 +441,34 @@ export const OverallAssessment: FC<OverallAssessmentProps> = ({
|
|
|
>
|
|
|
填写
|
|
|
</Button>
|
|
|
+ )}
|
|
|
+ {detail?.opinion ? (
|
|
|
+ <p style={{ marginTop: 10 }}>{detail.opinion}</p>
|
|
|
) : (
|
|
|
- <p>
|
|
|
- 1、
|
|
|
- 工作中具有独立思考、不断创新的能力。对工作有高度的事业心和责任感,积极主动工作,认真履行自己的职责。
|
|
|
- 2、
|
|
|
- 该同志工作态度端正,对工作有高度的事业心和责任感,政治立场坚定,方向明确,忠诚并献身于党和人民的教育事业,治学严谨,热爱学生,以身作则,为人师表。
|
|
|
- 3、
|
|
|
- 该同志遵纪守法,具有良好的社会公德和家庭美德,有高尚的职业道德,关爱学生,面向全体学生,注重学生的全面发展,认真履行教师职务职责,有强烈的服务意识。
|
|
|
- 4、
|
|
|
- 该同志政治立场坚定,教育思想端正,专业知识及基本功扎实过硬,具有很强的表达能力,善于做思想政治工作。严格遵守学校的作息时间和工作纪律,全身心地投入工作。
|
|
|
- </p>
|
|
|
+ <Empty />
|
|
|
)}
|
|
|
</Form.Item>
|
|
|
</>
|
|
|
)}
|
|
|
|
|
|
<Form.Item label="资料上传">
|
|
|
- {(detail?.materials?.filter((i) => i.module === "assess") || []).map(
|
|
|
- (item) => {
|
|
|
- const isUpload = item.isUpload === YES_OR_NO.YES;
|
|
|
+ {!materials.length && <Empty />}
|
|
|
+
|
|
|
+ {materials.map((item) => {
|
|
|
+ const isUpload = item.isUpload === YES_OR_NO.YES;
|
|
|
|
|
|
- return (
|
|
|
- <div key={item.id} className={style.fileUpload}>
|
|
|
- <div className={style.fileUploadHeader}>
|
|
|
- <div>
|
|
|
- <Tag color={isUpload ? "red" : ""}>
|
|
|
- {isUpload ? "必填" : "选填"}
|
|
|
- </Tag>
|
|
|
- <h3>
|
|
|
- {item.name} | {item.suffix || "*"}
|
|
|
- </h3>
|
|
|
- </div>
|
|
|
+ return (
|
|
|
+ <div key={item.id} className={style.fileUpload}>
|
|
|
+ <div className={style.fileUploadHeader}>
|
|
|
+ <div>
|
|
|
+ <Tag color={isUpload ? "red" : ""}>
|
|
|
+ {isUpload ? "必填" : "选填"}
|
|
|
+ </Tag>
|
|
|
+ <h3>
|
|
|
+ {item.name} | {item.suffix || "*"}
|
|
|
+ </h3>
|
|
|
+ </div>
|
|
|
+ {Boolean(item.filePath) && (
|
|
|
<Button
|
|
|
type="primary"
|
|
|
ghost
|
|
|
@@ -453,55 +477,52 @@ export const OverallAssessment: FC<OverallAssessmentProps> = ({
|
|
|
baseUrl +
|
|
|
process.env.REACT_APP_IMG_PUBLIC +
|
|
|
item.filePath,
|
|
|
- item.fileName
|
|
|
+ item.fileName,
|
|
|
)
|
|
|
}
|
|
|
>
|
|
|
下载模板
|
|
|
</Button>
|
|
|
- {canUpload && !isEvalutionDetail && !disabled && (
|
|
|
- <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={uploadedFileMap[item.id]}
|
|
|
- columns={fileColumns}
|
|
|
- />
|
|
|
+ )}
|
|
|
+ {canUpload && !isEvalutionDetail && !disabled && (
|
|
|
+ <DageUploadProvider>
|
|
|
+ <DageUploadConsumer>
|
|
|
+ {(res) => (
|
|
|
+ <DageUpload
|
|
|
+ key={uploadKeys[item.id] || 0}
|
|
|
+ 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={uploadedFileMap[item.id]}
|
|
|
+ columns={fileColumns}
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ );
|
|
|
+ })}
|
|
|
</Form.Item>
|
|
|
|
|
|
{!isReportDetail && (
|
|
|
@@ -551,6 +572,7 @@ export const OverallAssessment: FC<OverallAssessmentProps> = ({
|
|
|
|
|
|
<SubEvaluationModal
|
|
|
open={subEvaluationVisible}
|
|
|
+ item={checkedAddItem.current}
|
|
|
onOk={handleAdditionalEvalution}
|
|
|
onCancel={() => setSubEvaluationVisible(false)}
|
|
|
/>
|