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} >
{data.map((item, idx) => (data.length == 1 ? CascaderNode('') : CascaderNode(idx)))} {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);