import React, { useEffect, useState, useRef, useMemo, useCallback } from 'react';
import '@ant-design/compatible/assets/index.css';
import {
Modal,
Input,
Select,
message,
Cascader,
Form,
Tabs,
Row,
Col,
Empty,
Button,
Steps,
Popover,
Upload,
Table,
Divider,
Collapse,
} from 'antd';
import { PlusOutlined, UploadOutlined } from '@ant-design/icons';
import { connect } from 'dva';
import { useForm } from 'rc-field-form';
import AuditDetailed from './AuditDetailed';
import AuditFlow from './AuditFlow';
import {
queryDingSchema,
queryGetBomForm,
queryProcessFlows,
querySaveBomForm,
} from '@/services/boom';
import { Form as Form3x } from '@ant-design/compatible';
import { getCurrentUser } from '@/utils/authority';
import DDCode from '@/components/DDComponents/DDCode';
import { uploadFile, queryUserListByRoleID } from '@/services/boom';
import ApprovalProcess from './ApprovalProcess';
import { uuidv4 } from '@antv/xflow';
import AliyunOSSUpload from '@/components/OssUpload/AliyunOssUploader';
import AttachmentTable from '@/components/AttachmentTable';
import { getToken } from '@/utils/utils';
import LuckyExcel from 'luckyexcel';
import DDComponents from '@/components/DDComponents';
const { TextArea } = Input;
const { Option } = Select;
const { TabPane } = Tabs;
const { Step } = Steps;
// 提交
function CommitAuditModal(props) {
const {
dispatch,
visible,
onClose,
// loading,
version,
versionList,
flowDetail,
currentUser,
luckysheet,
userList,
templateId,
projectList,
OSSData,
} = props;
const [auditId, setAuditId] = useState();
const [data, setData] = useState([]);
const [length, setLength] = useState(1);
const [formData, setFromData] = useState({});
const [auditList, setAuditList] = useState([]); //用于创建Tabs表单
const [formComponentValues, setFormComponentValues] = useState({}); //用于创建Tabs表单
const [form] = Form.useForm();
const [aduitDetailForm] = Form.useForm();
const [approvalProcess, setApprovalProcess] = useState({});
const [selectUserList, setSelectUserList] = useState([]);
const [curNodeIdx, setCurNodeIdx] = useState(-1);
const [currentTab, setCurrentTab] = useState(0);
const [dataSource, setDataSource] = useState([]);
const [loading, setLoading] = useState(false);
const uploadList = useRef([]);
const formValueRef = useRef({
form: [],
});
function getDataValue(item) {
let arr = [];
arr.push(item.value);
if (item.children?.length > 0) {
const res = getDataValue(item.children[0]);
arr = arr.concat(res);
}
return arr;
}
const initFormList = async () => {
const res = await queryGetBomForm({
project_id: version.project_id,
node_id: version.template_node_id,
});
if (res.data) {
const formList = JSON.parse(res.data.json);
setApprovalProcess(formList.approvalProcess || {});
const prevFormData = JSON.parse(formList.formList?.[0] || '{}');
setFormComponentValues(prevFormData);
return formList;
}
return [];
};
/**
*
* @param {*} currentId 当前节点
* @param {*} type 下一个节点的类型 custom-circle: 审批节点 custom-rect: 业务节点
* @returns
*/
const getNextNodes = (currentId, type) => {
const { edges, nodes } = flowDetail;
if (!currentId) return [];
// 删除虚线通向的节点
// let targetIds = edges
// .filter(edge => {
// let line = edge.attrs?.line?.strokeDasharray?.split(' ');
// return edge.source.cell == currentId && line && line[0] == '0';
// })
// .map(item => item.target.cell);
// console.log(
// '---------',
// edges.filter(edge => edge.source.cell == currentId)
// );
const targetIds = edges
.filter(edge => edge.source.cell === currentId)
.map(item => item.target.cell);
edges.filter(edge => edge.source.cell === currentId);
const auditNodes = nodes.filter(node => {
if (type && node.name !== type) {
return false;
}
return targetIds.indexOf(node.id) !== -1;
});
const auditNode = auditNodes.map(item => {
return {
label: item.label,
value: item.Id,
Id: item.node_id,
parentId: currentId,
children: [],
};
});
return auditNode || [];
};
const treeData = currentId => {
const list = getNextNodes(currentId, 'custom-circle');
const fun = nodes => {
const re = nodes?.forEach(item => {
const nextNodes = getNextNodes(item.Id, 'custom-circle');
if (nextNodes || nextNodes.length > 0) list.push(...nextNodes);
fun(nextNodes);
});
};
fun(list);
const fun2 = listParams => {
const parents = listParams.filter(
item => listParams.findIndex(node => node.Id == item.parentId) == -1
);
const translator = (p, children) => {
setLength(length + 1);
p.forEach(parent => {
children.forEach((current, index) => {
if (current.parentId === parent.Id) {
const temp = JSON.parse(JSON.stringify(children));
temp.splice(index, 1);
translator([current], temp);
if (!parent.children.find(item => item.Id === current.Id))
parent.children.push(current);
}
});
});
};
translator(parents, listParams);
return parents;
};
return fun2(list);
};
const changeAudit = id => {
const node = flowDetail.nodes.find?.(item => item.Id === id);
setAuditId(node?.node_id);
};
const getFromData = idList => {
const data = formComponentValues;
const result = [];
//获取流转节点的层级关系
let len = 0;
let list = [];
idList.forEach(item => {
if (len < item.length) len = item.length;
});
for (let i = 0; i < len; i++) {
idList.forEach(item => {
if (item && item[i]) list.push(item[i]);
});
}
let firstList = [...new Set(list)];
// let attachment = await upload();
firstList.forEach(id => {
let approvalNode = flowDetail.nodes.find?.(item => item.Id == id);
if (!approvalNode) return;
const prevValues = data.length
? data.find(item => item.template_node_id === approvalNode.Id).formComponentValues
: [];
let values = data[approvalNode.Id] || prevValues || [];
let audit_list = [],
cc_list = [];
approvalProcess[approvalNode.Id]?.forEach(item => {
let arr = item[0].is_cc == 1 ? cc_list : audit_list;
if (item[0].type == 'role') return arr.push(item[0].nowValue);
return arr.push(item[0].value);
});
const formItem = {
flow_id: approvalNode.flow_id,
template_node_id: approvalNode.Id,
formComponentValues: [...values], // { name: '附件', value: JSON.stringify(attachment) }
audit_list,
cc_list,
};
result.push(JSON.stringify(formItem));
});
return result;
};
// 填写表单实时计算审批流程
const advanceSubmit = async () => {
// console.log('重重新计算审批流程');
const fieldsValue = await form.validateFields();
let hasFlowId = true; //是否都绑定审批节点
const result = Object.values(fieldsValue)
.map(item => {
if (item && Array.isArray(item)) return item;
})
.filter(item => item);
const formList = await getFromData(result);
let params = {
desc: fieldsValue.desc,
// 审核流程id
flow_id: 0,
node_level_id: 0,
id: version.id,
project_id: version.project_id,
cur_template_node_id: version.template_node_id * 1, // 当前节点
next_template_node_id: 0, // 审核完成后的业务节点
template_node_id: null, // 将要流转的节点审批节点
flow_path: null, // 审批节点数组
// 模板id.一致就行
template_id: version.template_id,
cur_template_id: version.template_id,
next_template_id: version.template_id,
form_list: formList,
};
dispatch({
type: 'detail/advanceSubmitNextNode',
payload: params, // values,
callback: data => {
if (data) {
setApprovalProcess(data);
}
},
});
};
// 处理tabs页
const setAuditListFun = async (approvalProcess = {}, prevFormData = []) => {
const fieldsValue = await form.validateFields();
let addAuditList = [];
const result = Object.values(fieldsValue)
.map(item => {
if (item && Array.isArray(item)) return item;
})
.filter(item => item)
.flat(Infinity);
const nodeList = [...new Set(result)]
.map(Id => {
return flowDetail.nodes.find?.(item => item.Id == Id);
})
.filter(item => item);
const flowIds = [...new Set(nodeList.map(item => item.flow_id))].join(',');
const processFlows = await queryProcessFlows({ ids: flowIds });
if (processFlows && processFlows?.length > 0) {
const newlist = nodeList.map(node => {
const curData = processFlows.find(item => item.id === node.flow_id);
const newItem = {
name: curData?.name,
nodeId: node.Id,
items: JSON.parse(curData?.form_json || '[]'),
};
return newItem;
});
addAuditList = [...addAuditList, ...newlist];
}
addAuditList.forEach(addAuditItem => {
// 回填部分组件的历史数据
if (prevFormData.length) {
const currentForm = prevFormData.find(
pItem => pItem.template_node_id === addAuditItem.nodeId
);
addAuditItem.items.forEach(DDComponent => {
const prevValue = currentForm?.formComponentValues?.find(
cItem => cItem.id === DDComponent.props.id
);
DDComponent.props.defaultValue = prevValue?.value || prevValue?.defaultValue;
});
}
// 回填之前的值结束后需要把formComponentValues清空 防止提交之前的值
setFormComponentValues([]);
// const tempFormComponentValues = JSON.parse(JSON.stringify(formComponentValues));
// const Components = Form3x.create({
// onValuesChange: (props, changedValues, allValues) => {
// const { items: allFormItem } = props;
// tempFormComponentValues[addAuditItem.nodeId] = allFormItem
// .map(formItem => {
// const itemProps = formItem.props;
// const val = allValues[itemProps.id];
// if (!itemProps.label || val === '') return;
// if (val instanceof Object) {
// return {
// name: itemProps.label,
// id: itemProps.id,
// value: [...val],
// };
// } else if (allValues[itemProps.id]) {
// return {
// name: itemProps.label,
// id: itemProps.id,
// value: [allValues[itemProps.id]] || undefined,
// };
// }
// })
// .filter(formItem => formItem);
// if (getReComputeAudit(allFormItem, changedValues)) advanceSubmit();
// console.log(tempFormComponentValues);
// setFormComponentValues({ ...tempFormComponentValues });
// },
// })(AuditDetailed);
// addAuditItem.FormComponents = ;
});
setAuditList(addAuditList);
advanceSubmit();
};
const onChange = async (value, approvalProcess) => {
// 加载之前提交的form数据
const resFormData = await initFormList();
const resData = resFormData?.formList;
const prevFormData =
resData && resData.length ? resData.map(resItem => JSON.parse(resItem)) : null;
if (prevFormData && prevFormData.length) {
const formValues = {};
prevFormData.forEach(pItem => {
formValues[pItem.template_node_id] = [...pItem.formComponentValues];
});
setFormComponentValues(formValues);
}
if (value) {
changeAudit(value[value.length - 1]);
if (prevFormData !== null) {
setAuditListFun(approvalProcess, prevFormData);
} else {
setAuditListFun(approvalProcess);
}
} else {
changeAudit('');
setAuditList([]);
setApprovalProcess({});
}
form.setFieldValue('next_template_node_id', '');
};
useEffect(() => {
if (!visible) return;
const { edges, nodes } = flowDetail;
const Id = version.template_node_id;
const currentId = flowDetail.nodes.find?.(item => item.Id == Id)?.node_id;
const data = treeData(currentId);
const nextNodes = getNextNodes(currentId, 'custom-rect');
if (data.length <= 0 || nextNodes.length > 0) {
setAuditId(currentId);
} else {
const defaultValues = {};
if (data.length === 1) {
const value = getDataValue(data[0]);
defaultValues.circle = value;
} else {
data.forEach((item, index) => {
const value = getDataValue(item);
defaultValues[`circle${index}`] = value;
});
}
// 设置延迟,等待组件渲染
setTimeout(async () => {
form.setFieldsValue(defaultValues);
const initForm = await initFormList();
const tempAP = initForm?.approvalProcess ? initForm.approvalProcess : {};
Object.values(defaultValues).forEach(value => onChange(value, tempAP));
}, 200);
}
setData(data);
// });
}, [version.template_node_id, visible]);
useEffect(() => {
if (!visible) return;
dispatch({
type: 'detail/getChartOSSData',
payload: {
projectId: version.project_id,
},
});
}, [visible]);
useEffect(() => {
form.resetFields();
setAuditList([]);
}, [visible]);
useEffect(() => {
if (!visible) {
// 清空数据
uploadList.current = [];
}
}, [visible]);
// const OnModelFileDone = file => {
// var path = OSSData.host + '/' + file.url;
// uploadList.current = [...uploadList.current, path];
// console.log(uploadList.current);
// };
const setUploadList = files => {
uploadList.current = files.map(file => OSSData.host + '/' + file.url);
// console.log(uploadList.current);
};
const OnUploading = file => {};
const uploadProps = {
OSSData: OSSData,
// onDone: OnModelFileDone,
onUploading: OnUploading,
noStyle: false,
onChange: setUploadList,
// showUploadList: false,
};
const currentNodeId = useMemo(() => {
let Id = version.template_node_id;
setAuditId(currentNodeId);
return flowDetail.nodes.find?.(item => item.Id == Id)?.node_id;
}, [flowDetail, version]);
const nextNodesList = useMemo(() => {
if (!auditId && !currentNodeId) return [];
return getNextNodes(auditId || currentNodeId, 'custom-rect');
}, [auditId, currentNodeId, flowDetail]);
const getReComputeAudit = (items, changedValues) => {
const id = Object.keys(changedValues)[0];
const formItem = items?.find(item => item.props.id == id);
if (formItem && formItem.props?.required) return true;
return false;
};
const handleTabChange = key => {
const index = Number(key.split('_')[0]);
setCurrentTab(index);
};
const onDIYTableChange = (value, id, label) => {
const currentNodeID = auditList[currentTab].nodeId;
const oldData = formComponentValues[currentNodeID] || [];
const ids = id.split(';');
const [rowIndex, colIndex] = ids[0].split(',').map(item => Number(item));
const [columnID, tableID] = ids[1].split('>');
const [columnLabel, tableLabel] = label.split('>');
const cell = {
name: columnLabel,
id: columnID,
type: columnID.split('_')[0],
value: [value],
};
// 组装可能用到的数据
const cols = [];
cols[colIndex] = cell;
const rows = [];
rows[rowIndex] = cols;
if (oldData && oldData.length) {
const table = oldData.find(item => item.id === tableID);
if (table) {
const oldRows = table.value;
if (oldRows) {
const oldCols = oldRows[rowIndex];
if (oldCols) {
// 记录可编辑控件
table.value[rowIndex][colIndex] = cell;
const newData = oldData.map(item => {
if (item.id === table.id) {
return table;
}
return item;
});
setFormComponentValues(prevState => {
return { ...prevState, [currentNodeID]: newData };
});
} else {
table.value[rowIndex] = cols;
const newData = oldData.map(item => {
if (item.id === table.id) {
return table;
}
return item;
});
setFormComponentValues(prevState => {
return { ...prevState, [currentNodeID]: newData };
});
}
} else {
table.value = rows;
const newData = oldData.map(item => {
if (item.id === table.id) {
return table;
}
return item;
});
setFormComponentValues(prevState => {
return { ...prevState, [currentNodeID]: newData };
});
}
} else {
const newData = [
...oldData,
{
name: tableLabel,
id: tableID,
type: tableID.split('_')[0],
value: rows,
},
];
setFormComponentValues(prevState => {
return { ...prevState, [currentNodeID]: newData };
});
}
} else {
const newData = [
{
name: tableLabel,
id: tableID,
type: tableID.split('_')[0],
value: rows,
},
];
setFormComponentValues(prevState => {
return { ...prevState, [currentNodeID]: newData };
});
}
};
const onFormValueChange = (changedFields, allValues) => {
const currentNodeID = auditList[currentTab].nodeId;
const allFormItem = auditList[currentTab].items;
const componentValue = formComponentValues[currentNodeID] || [];
const currentFieldID = Object.keys(changedFields)[0];
const formItem = allFormItem.find(item => item.props.id === currentFieldID);
// 记录TextNote 以及其他未填写的值
for (let index = 0; index < allFormItem.length; index++) {
const tempFormItem = allFormItem[index];
// 跳过DIYTable控件
if (tempFormItem.componentName === 'DIYTable') {
continue;
}
// 没找到就给默认值
if (!componentValue.find(item => item.id === tempFormItem.props.id)) {
if (tempFormItem.componentName === 'TextNote') {
componentValue.push({
name: tempFormItem.props.label,
id: tempFormItem.props.id,
value: [tempFormItem.props.placeholder],
});
} else {
componentValue.push({
name: tempFormItem.props.label,
id: tempFormItem.props.id,
value: [],
});
}
}
}
// 记录变更的formItem值
if (componentValue.length) {
for (let index = 0; index < componentValue.length; index++) {
const item = componentValue[index];
if (item.id === currentFieldID) {
componentValue[index] = {
name: formItem.props.label,
id: currentFieldID,
value: [changedFields[currentFieldID]],
};
break;
} else if (index === componentValue.length - 1) {
componentValue.push({
name: formItem.props.label,
id: currentFieldID,
value: [changedFields[currentFieldID]],
});
}
}
} else {
componentValue.push({
name: formItem.props.label,
id: currentFieldID,
value: [changedFields[currentFieldID]],
});
}
setFormComponentValues({
...formComponentValues,
[currentNodeID]: componentValue,
});
formValueRef.current.form[currentNodeID] = [...componentValue];
advanceSubmit();
};
// const getFlowPath = node => {
// //[134, 135]
// let itemData = {};
// const Function = (curId, index) => {
// if (!curId) return;
// let data = {};
// let approvalNode = flowDetail.nodes.find?.(item => item.Id == curId);
// data.template_id = version.template_id;
// data.flow_id = approvalNode?.flow_id || 0;
// data.node_level_id = approvalNode?.flow_id ? 1 : 0;
// data.template_node_id = approvalNode?.Id;
// index++;
// if (approvalNode?.Id) {
// if (!approvalNode?.flow_id) {
// hasFlowId = false;
// }
// }
// const res = Function(node[index], index);
// if (res) {
// data.flow_path = [res];
// }
// return data;
// };
// itemData = Function(node[0], 0);
// return itemData;
// };
const onFinish = async () => {
const isOk = Object.values(approvalProcess).every(item => {
return item.every(cur => {
if (cur[0].type == 'role') return cur[0].nowValue;
return true;
});
});
if (!isOk) {
message.error('请选择审批人。');
return;
}
var fieldsValue = await form.validateFields();
let hasFlowId = true; //是否都绑定审批节点
const getFlowPath = node => {
//[134, 135]
let itemData = {};
const Function = (curId, index) => {
if (!curId) return;
let data = {};
let approvalNode = flowDetail.nodes.find?.(item => item.Id == curId);
data.template_id = version.template_id;
data.flow_id = approvalNode?.flow_id || 0;
data.node_level_id = approvalNode?.flow_id ? 1 : 0;
data.template_node_id = approvalNode?.Id;
index++;
if (approvalNode?.Id) {
if (!approvalNode?.flow_id) {
hasFlowId = false;
}
}
const res = Function(node[index], index);
if (res) {
data.flow_path = [res];
}
return data;
};
itemData = Function(node[0], 0);
return itemData;
};
let result = Object.values(fieldsValue)
.map(item => {
if (item && Array.isArray(item)) return item;
})
.filter(item => item);
let serviceNode = flowDetail.nodes.find?.(item => item.Id == fieldsValue.next_template_node_id);
if (!serviceNode) {
message.error('请选择需要流转的业务节点。');
return;
}
const flowPath = result.map(item => getFlowPath(item));
setLoading(true);
try {
const formList = getFromData(result);
let params = {
desc: fieldsValue.desc,
// 审核流程id
// flow_id: approvalNode?.flow_id || 0,
// node_level_id: approvalNode?.flow_id ? 1 : 0,
id: version.id,
project_id: version.project_id,
cur_template_node_id: version.template_node_id * 1, // 当前节点
next_template_node_id: serviceNode.Id * 1, // 审核完成后的业务节点
// template_node_id: result[0][0], // 将要流转的节点审批节点
// flow_path:flow_path, //审批节点数组
// 模板id.一致就行
template_id: version.template_id,
cur_template_id: version.template_id,
next_template_id: version.template_id,
};
if (serviceNode.node_type_psr == 3 || serviceNode.node_type_psr == 4) {
let project = projectList.find(item => item.id == version?.project_id);
let projectName = project?.project_name || '';
let sheetData = await uploadExcelByUrl(serviceNode.node_type_psr, version.id, projectName);
params.data = JSON.stringify(sheetData);
}
// params.data = await uploadExcelByUrl(3, version.id);
// console.log(params);
if (result.length <= 0) {
//直接走业务节点
} else if (result.length <= 1 && result[0]?.length <= 1) {
//单个审批节点
let approvalNode = flowDetail.nodes.find?.(item => item.Id == result[0][0]);
params.flow_id = approvalNode?.flow_id || 0;
params.node_level_id = approvalNode?.flow_id ? 1 : 0;
params.template_node_id = result[0][0]; // 将要流转的节点审批节点
params.form_list = formList; //创建钉钉表单所需数据
if (approvalNode?.Id) {
if (!approvalNode?.flow_id) {
hasFlowId = false;
}
}
} else {
//多节点审批
params.template_node_id = result[0][0]; // 将要流转的节点审批节点
params.flow_path = flowPath;
params.form_list = formList; //创建钉钉表单所需数据
}
if (!hasFlowId) {
message.error('当前存在审批节点未绑定审批流程!请联系管理员。');
return;
}
await querySaveBomForm({
project_id: version.project_id,
node_id: version.template_node_id,
json: JSON.stringify({ approvalProcess, formList }),
});
params.audit_series = uuidv4();
if (version.audit_status == 5) {
params.audit_status = version.audit_status;
}
params.files = uploadList.current.join(',');
onSubmitNextNode(params);
} catch (error) {
console.error(error);
}
};
const onSubmitNextNode = values => {
dispatch({
type: 'detail/submitNextNode',
payload: values,
callback: newVersion => {
setLoading(false);
onClose();
// 更新flow流程图
dispatch({
type: 'xflow/queryBoomFlowDetail',
payload: {
id: templateId,
},
});
},
});
};
const CascaderNode = index => {
return (
);
};
// const upload = async () => {
// let blob = await luckysheet.current.getExcelBolb();
// let formData = new FormData();
// formData.append('userid', currentUser.DingUserId);
// formData.append('file', new File([blob], `${version.version_name}_${version.version_no}.xlsx`));
//
// try {
// let res = await uploadFile(formData);
// let data = JSON.parse(res.dentry);
// return [
// {
// spaceId: String(data.spaceId),
// fileName: data.name,
// fileSize: String(data.spaceId),
// fileType: data.extension,
// fileId: data.id,
// },
// ];
// } catch (error) {
// message.error('附件上传失败');
// }
// };
//
// const columns = [
// {
// title: '文件名称',
// dataIndex: 'name',
// key: 'name',
// },
// {
// title: '操作',
// render: record => 删除,
// },
// ];
return (
{
setAuditId();
onClose();
}}
onOk={onFinish}
>
{OSSData.host && }
{auditList.map((item, idx) => (
{!approvalProcess[item.nodeId] ? ( // !formComponentValues[item.nodeId] ||
) : (
)}
))}
);
}
const uploadExcelByUrl = (nodeType, versionId) => {
const TEMPLATE_URL =
'https://water-service-test.oss-cn-hangzhou.aliyuncs.com/doc/contract/2023-06-29/ed0d5dcd-6ce0-40df-9d17-a1f69245dbb9.xlsx';
const TEMPLATE_URL2 =
'https://water-service-test.oss-cn-hangzhou.aliyuncs.com/doc/contract/2023-06-29/431733cd-0abc-4a68-a439-d24c466e9845.xlsx';
return new Promise((reslove, reject) => {
LuckyExcel.transformExcelToLuckyByUrl(
nodeType == 3 ? TEMPLATE_URL : TEMPLATE_URL2,
'模板.xlsx',
async (exportJson, luckysheetfile) => {
let [record] = await getExcel(versionId);
let len = exportJson.sheets.length;
const excelData = exportJson.sheets?.map(item => {
return { ...item, order: Number(item.order) };
});
delete record.id;
record.order = len;
record.index = String(len);
record.status = '0';
record.name = '投标成本';
var res = [...excelData, record];
// console.log(res);
reslove(JSON.stringify(res));
}
);
});
};
async function getExcel(gridKey) {
var formData = new FormData();
formData.append('gridKey', gridKey);
let res = await fetch(
`/api/v1/purchase/record/sheet?gridKey=${gridKey}&JWT-TOKEN=${getToken()}`,
{
method: 'POST',
body: formData,
}
).then(response => response.text());
return JSON.parse(JSON.parse(res));
}
export default connect(({ xflow, detail, user, list }) => ({
flowDetail: xflow.flowDetail,
versionList: detail.versionList,
currentUser: user.currentUser,
userList: user.list,
OSSData: detail.OSSData,
// 隐患:刷新页面后将会丢失projectList
projectList: list?.project?.list || [],
}))(CommitAuditModal);