<template> <div class="processApproval" ref="processApprovalDom"> <div class="left-card"> <div class="top"> <div class="commons-title title"> {{ getFlowName() }} </div> <div class="line"></div> <!-- 报价单 --> <template v-if="flowForm.flowKey == 'sale_quotation_flow'"> <!-- <PriceSheetEHSD ref="makeDom" :queryData="queryData.data"></PriceSheetEHSD> --> <PriceSheet ref="makeDom" :queryData="queryData.data"></PriceSheet> </template> <!-- 销售合同 --> <Contract ref="makeDom" :queryData="queryData.data" v-else-if="flowForm.flowKey == 'contract_flow'"></Contract> <!-- 销售合同变更 --> <ContractChange ref="makeDom" :queryData="queryData.data" v-else-if="flowForm.flowKey == 'contract_update_flow'"></ContractChange> <!-- 申购 --> <Subscribe ref="makeDom" :queryData="queryData.data" v-else-if="flowForm.flowKey == 'subscribe_flow'"></Subscribe> <!-- 采购 --> <Purchase ref="makeDom" :queryData="queryData.data" v-else-if="flowForm.flowKey == 'purchase_flow'"> </Purchase> <!-- 采购变更 --> <PurchaseChange ref="makeDom" :queryData="queryData.data" v-else-if="flowForm.flowKey == 'purchase_update_flow'"> </PurchaseChange> <!-- 采购付款 --> <PurchasePayment ref="makeDom" v-else-if="flowForm.flowKey == 'pay_flow'" :queryData="queryData.data"></PurchasePayment> <!-- 请款 --> <SendFunds ref="makeDom" v-else-if="flowForm.flowKey == 'account_request_funds_flow'" :queryData="queryData.data"></SendFunds> <ReturnGood ref="makeDom" v-else-if="flowForm.flowKey == 'purchase_back_flow'" :queryData="queryData.data"></ReturnGood> <!-- 取消到账认领 --> <!-- <CancelClaim ref="makeDom" v-else-if="flowForm.flowKey == 'claim_del_flow'" :queryData="queryData.data"></CancelClaim> --> </div> </div> <div class="right-card"> <el-tabs v-model="activeName" class="demo-tabs"> <el-tab-pane label="处理意见" name="first" v-if="isShowSubmitDom"> <div style="overflow: auto; height: calc(100vh - 200px)"> <div style="padding-bottom:50px" v-if="showChart"> <div style="margin-bottom:10px;"> <TitleInfo :content="'利润预算波动'"></TitleInfo> </div> <div ref="chartDom" style="height:150px"></div> </div> <div style="margin-bottom:10px"> <TitleInfo :content="'办理'"></TitleInfo> </div> <el-form :model="flowForm" :rules="flowRules" ref="flowFormDom" label-position="top"> <el-form-item prop="remark" label-width="0px" label=""> <el-input type="textarea" placeholder="请输入" :rows="3" v-model="flowForm.remark"> </el-input> </el-form-item> <el-form-item prop="remark" label=""> <div style="width:100%"> <el-upload v-model:fileList="flowForm.fileList" drag :action="uploadUrl" :data="uploadData" multiple :before-upload="uploadFile" :on-success="handleSuccess" :on-preview="onPreviewFile"> <el-icon class="el-icon--upload"><upload-filled /></el-icon> <div class="el-upload__text"> 点击或拖拽上传附件 </div> </el-upload> </div> </el-form-item> <el-form-item> <el-button type="primary" v-if="approvalRecordData.buttonInfoList.length == 0" @click="handleSubmit" :loading="btnLoading">提交</el-button> <el-button type="primary" v-else v-for="i in approvalRecordData.buttonInfoList" :key="i.type" :loading="btnLoading" @click="handleSubmit(i.type)" style="margin-bottom:10px">{{ i.name }}</el-button> </el-form-item> </el-form> </div> </el-tab-pane> <el-tab-pane label="审批记录" name="second"> <ul class="flow-chart" ref="flowChartDom"> <li v-for="item in recordList" :key="item.id" :class=" !route.query.id ? '' : item.status == 2 ? 'flow-orange' : item.status == 3 && !route.query.id ? 'flow-orange' : item.status == 3 && route.query.id ? 'flow-grey' : '' "> <div class="left-icon"> <i class="iconfont icon-iconm_daick"></i> <i class="iconfont icon-icomx_quertj1 right-btm-status"></i> </div> <div class="right-conetnt"> <div class="name">{{ item.nodeName }}</div> <div class="remark"> <div class="label"> <span v-if="item.status != 3">办理人:</span>{{ item.processedUser }}<span class="time">{{ item.processedDate }}</span> </div> {{ item.remark }} <div v-for="j in fileObj[item.flowExampleDetailId]" v-if="fileObj[item.flowExampleDetailId]"> <a @click="proxy.download(j.fileUrl, j.fileName)" style="color: #409eff; line-height: 30px">{{ j.fileName }}</a> </div> </div> </div> <div class="line"></div> </li> </ul> </el-tab-pane> <!-- <el-tab-pane label="决策辅助" name="three" v-if="auxiliaryData.length > 0"> <div style="overflow: auto; height: calc(100vh - 200px)"> <auxiliary :data="auxiliaryData"></auxiliary> </div> </el-tab-pane> --> </el-tabs> </div> <el-dialog title="下一处理人" width="400" v-model="dialogVisible" v-if="dialogVisible" :show-close="true" :close-on-click-modal="false" :close-on-press-escape="false"> <el-form :model="flowForm"> <el-form-item prop="remark" label="处理人"> <el-select v-model="flowForm.handleUserId" placeholder="请选择" filterable style="width: 100%"> <el-option v-for="item in nextHandleUser" :label="item.nickName" :value="item.userId"> </el-option> </el-select> </el-form-item> <el-form-item> <div style="width: 100%; text-align: center"> <el-button type="primary" @click="handleSelectUser">提交</el-button> </div> </el-form-item> </el-form> </el-dialog> </div> </template> <script setup name="ProcessApproval"> import useTagsViewStore from "@/store/modules/tagsView.js"; import { useRouter, useRoute } from "vue-router"; import PriceSheet from "@/components/process/SF/PriceSheet"; import Contract from "@/components/process/SF/Contract"; import ContractChange from "@/components/process/SF/ContractChange"; import Subscribe from "@/components/process/SF/Subscribe"; import Purchase from "@/components/process/SF/Purchase"; import PurchaseChange from "@/components/process/SF/PurchaseChange"; import ReturnGood from "@/components/process/SF/ReturnGood"; import PurchasePayment from "@/components/process/PurchasePayment"; import SendFunds from "@/components/process/SendFunds"; // 消息提示 import { ElMessage, ElMessageBox } from "element-plus"; //决策辅助 import auxiliary from "./auxiliary"; import useUserStore from "@/store/modules/user"; const userInfo = useUserStore(); import $bus from "@/bus/index.js"; import * as echarts from "echarts"; import TitleInfo from "@/components/TitleInfo/index.vue"; const router = useRouter(); const route = useRoute(); // 传参 const props = defineProps({ query: Object, }); // tab切换逻辑 const activeName = ref("first"); let auxiliaryData = ref([]); const getAuxiliaryData = (data) => { auxiliaryData.value = data; }; const btnLoading = ref(false); // 意见表单 const flowForm = reactive({ flowKey: "", handleUserId: "", remark: "", data: {}, fileList: [], }); const uploadData = ref({}); const flowRules = reactive({ // remark: [{ required: true, message: "请输入处理意见", trigger: "blur" }], }); //组件实例 const { proxy } = getCurrentInstance(); const makeDom = ref(null); const flowFormDom = ref(null); let dialogVisible = ref(false); const nextHandleUser = ref([]); const handleType = ref(null); const handleSelectUser = () => { if (!flowForm.handleUserId) { return ElMessage({ message: "请选择下一节点处理人!", type: "info", }); } handleSubmit(handleType.value); }; const handleResult = (res) => { if (res.userList == null && res.success) { skipPage(); } else if (res.userList && res.userList.length > 0) { dialogVisible.value = true; nextHandleUser.value = res.userList; } else { return ElMessage({ message: "请联系管理员!", type: "info", }); } }; const uploadFile = async (file) => { const res = await proxy.post("/fileInfo/getSing", { fileName: file.name }); uploadData.value = res.uploadBody; file.id = res.id; file.fileName = res.fileName; file.fileUrl = res.fileUrl; file.uploadState = true; return true; }; const handleSuccess = (any, UploadFile) => { UploadFile.raw.uploadState = false; }; const onPreviewFile = (file) => { window.open(file.raw.fileUrl, "_blank"); }; // 提交逻辑 const handleSubmit = async (_type) => { handleType.value = _type ? _type : undefined; try { // 调用发起组件的提交事件 const flag = await makeDom.value.handleSubmit(); if (flag) { flowFormDom.value.validate((valid) => { btnLoading.value = true; if (valid) { const data = { ...makeDom.value.getFormData() }; if (flowForm.flowKey == "subscribe_flow") { } else if (flowForm.flowKey == "account_request_funds_flow") { } else if (flowForm.flowKey == "sale_quotation_flow") { } else if (flowForm.flowKey == "contract_flow") { } else if (flowForm.flowKey == "contract_update_flow") { data.dataJson = JSON.stringify(proxy.deepClone(data)); if (data.fileList && data.fileList.length > 0) { data.fileList = data.fileList.map((item) => { return { id: item.raw.id, fileName: item.raw.fileName, fileUrl: item.raw.fileUrl, }; }); } else { data.fileList = []; } if (data.packageFileList && data.packageFileList.length > 0) { data.packageFileList = data.packageFileList.map((item) => { return { id: item.raw.id, fileName: item.raw.fileName, fileUrl: item.raw.fileUrl, }; }); } else { data.packageFileList = []; } data.ehsdJson = JSON.stringify({ deliveryTime: data.deliveryTime, }); data.contractProductList = data.contractProductList.map((item) => { let ehsdJson = JSON.stringify({ packMethod: item.packMethod, tradeMethods: item.tradeMethods, }); return { ...item, ehsdJson: ehsdJson, }; }); } else if (flowForm.flowKey == "sample_flow") { data.dataJson = JSON.stringify(proxy.deepClone(data)); if (data.fileList && data.fileList.length > 0) { data.fileList = data.fileList.map((item) => { return { id: item.raw.id, fileName: item.raw.fileName, fileUrl: item.raw.fileUrl, }; }); } else { data.fileList = []; } if (data.packageFileList && data.packageFileList.length > 0) { data.packageFileList = data.packageFileList.map((item) => { return { id: item.raw.id, fileName: item.raw.fileName, fileUrl: item.raw.fileUrl, }; }); } else { data.packageFileList = []; } data.ehsdJson = JSON.stringify({ deliveryTime: data.deliveryTime, }); data.sampleProductList = data.sampleProductList.map((item) => { let ehsdJson = JSON.stringify({ packMethod: item.packMethod, tradeMethods: item.tradeMethods, }); return { ...item, ehsdJson: ehsdJson, }; }); } else if (flowForm.flowKey == "sample_update_flow") { data.dataJson = JSON.stringify(proxy.deepClone(data)); if (data.fileList && data.fileList.length > 0) { data.fileList = data.fileList.map((item) => { return { id: item.raw.id, fileName: item.raw.fileName, fileUrl: item.raw.fileUrl, }; }); } else { data.fileList = []; } if (data.packageFileList && data.packageFileList.length > 0) { data.packageFileList = data.packageFileList.map((item) => { return { id: item.raw.id, fileName: item.raw.fileName, fileUrl: item.raw.fileUrl, }; }); } else { data.packageFileList = []; } data.ehsdJson = JSON.stringify({ deliveryTime: data.deliveryTime, }); data.sampleProductList = data.sampleProductList.map((item) => { let ehsdJson = JSON.stringify({ packMethod: item.packMethod, tradeMethods: item.tradeMethods, }); return { ...item, ehsdJson: ehsdJson, }; }); } flowForm.fileList = flowForm.fileList.map((item) => { return { ...item, ...item.raw, }; }); if (route.query.processType == 10 || route.query.processType == 30) { // if (_type && _type == 1) { // proxy // .post("/flowExample/setStartData", { // exampleId: route.query.id, // startData: data, // }) // .then(); // } proxy .post("/flowProcess/jump", { ...flowForm, data, handleType: _type, version: route.query.version, flowId: route.query.id, skipSetData: true, }) .then( (res) => { handleResult(res); }, (err) => { btnLoading.value = false; } ); return; } else { proxy .post("/flowProcess/initiate", { ...flowForm, data, skipSetData: true, }) .then( (res) => { handleResult(res); }, (err) => { btnLoading.value = false; } ); } } }); } } catch (err) { console.log("数据未填完整!", err); } }; // 页面跳转 const skipPage = () => { let pageObj = { subscribe_flow: "Subscribe", contract_flow: "Contract", contract_update_flow: "Contract", sale_quotation_flow: "Quotation", purchase_update_flow: "Purchased", purchase_flow: "Purchase", pay_flow: "Payment", purchase_back_flow: "ReturnGood", }; const useTagsStore = useTagsViewStore(); useTagsStore.delVisitedView(router.currentRoute.value); if (route.query.processType) { router.replace({ name: "Backlog", }); } else { proxy.msgTip("操作成功"); router.replace({ name: pageObj[route.query.flowKey], }); } $bus.emit("refreshTableData"); }; let queryData = reactive({ data: {}, }); // 记录 const recordList = ref([]); const fileIds = ref([]); const fileObj = ref({}); const approvalRecordData = ref({ buttonInfoList: [], }); const getRecords = (_id) => { if (_id) { proxy .post("/flowExample/getApprovalRecord", { id: _id, }) .then((res) => { recordList.value = res.recordList; queryData.data.recordList = res.recordList; approvalRecordData.value = res; fileIds.value = res.recordList.map((item) => { return item.flowExampleDetailId; }); proxy .post("fileInfo/getList", { businessIdList: fileIds.value }) .then((res2) => { fileObj.value = res2; }); }); } else { proxy .post("/flowExample/getFlowNode", { flowKey: flowForm.flowKey, }) .then((res) => { recordList.value = res; }); } }; const isShowSubmitDom = ref(true); const chartDom = ref(null); const chartData = ref([]); let myChart = null; const optionTwo = reactive({ data: { tooltip: { trigger: "axis", }, // legend: { // data: ["价格"], // }, grid: { left: "3%", right: "6%", top: "10%", bottom: "3%", containLabel: true, }, tooltip: { show: true, trigger: "axis", // 格式化函数 // valueFormatter: (val) => { // return val + "aaa"; // }, formatter: (params, ticket, callback) => { return ` ${params[0].axisValue} <br/> ${params[0].seriesName}:${params[0].data} <br/> 销售合同金额:${ chartData.value[params[0].dataIndex].contractCurrency } ${chartData.value[params[0].dataIndex].contractAmount} <br/> 采购合同金额:CNY ${ chartData.value[params[0].dataIndex].purchaseAmount }`; }, textStyle: { fontSize: 12, }, }, // toolbox: { // feature: { // saveAsImage: {}, // }, // }, xAxis: { type: "category", boundaryGap: false, data: [], }, yAxis: { type: "value", }, series: [ { name: "毛利", type: "line", data: [], }, ], }, }); const showChart = ref(false); onMounted(async () => { if (route.query.processType == 10 || route.query.processType == 30) { showChart.value = true; } // 路由进入 if (route.query && route.query.flowKey) { //processType 10 为修改 20为查看 30回退发起 if (route.query.processType == 20) { activeName.value = "second"; isShowSubmitDom.value = false; } if ( route.query.processType == 10 || route.query.processType == 20 || route.query.processType == 30 ) { // await proxy // .post("/flowProcess/getStartData", { flowId: route.query.id }) // .then((res) => { // queryData.data = { ...res }; // }); } else { queryData.data = { ...route.query }; } flowForm.flowKey = route.query.flowKey; flowForm.submitType = route.query.submitType; getRecords(route.query.id); } $bus.on("getGrossData", (data) => { nextTick(() => { if (data && data.length > 1 && showChart.value) { myChart = echarts.init(chartDom.value); window.addEventListener("resize", () => { myChart.resize(); }); chartData.value = data; // chartData.value = data.slice(1, data.length); optionTwo.data.xAxis.data = chartData.value.map((item) => { return item.createTime.slice(0, 10); }); optionTwo.data.series[0].data = chartData.value.map((item) => { return item.gross; }); myChart.setOption(optionTwo.data); myChart.resize(); } else { return; } }); }); }); onBeforeUnmount(() => { // 取消订阅特定事件 $bus.off("getGrossData"); }); watch( () => props.query, () => { if (props.query) { if (props.query.processType == 20) { isShowSubmitDom.value = false; activeName.value = "second"; } if ( props.query.processType == 10 || props.query.processType == 20 || props.query.processType == 30 ) { queryData.data = { ...props.query }; } else { queryData.data = { ...props.query }; } flowForm.flowKey = props.query.flowKey; flowForm.submitType = props.query.submitType; getRecords(props.query.id); } }, { deep: true, immediate: true, } ); const getFlowName = () => { let name = "流程(审批)"; if (route.query && route.query.flowName) { name = route.query.flowName; } else if (props.query && props.query.flowName) { name = props.query.flowName; } return name; }; // 动态计算高度 const processApprovalDom = ref(null); const flowChartDom = ref(null); const getTableHeight = () => { if (route.query.currentContractId) { processApprovalDom.value.style.height = window.innerHeight - 160 + "px"; flowChartDom.value.style.height = window.innerHeight - 250 + "px"; } else { processApprovalDom.value.style.height = window.innerHeight - 100 + "px"; flowChartDom.value.style.height = window.innerHeight - 210 + "px"; } }; onMounted(() => { getTableHeight(); }); window.addEventListener("resize", () => { getTableHeight(); }); </script> <style lang="scss" scoped> .processApproval { display: flex; justify-content: space-between; padding: 10px; // height: calc(100vh - 100px); .left-card { // background: #fff; border-radius: 4px; // padding: 20px; // flex: 1; width: calc(100% - 350px - 10px); margin-right: 10px; display: flex; flex-direction: column; .top { /* 宽度和高度 */ flex: 1; overflow-y: auto; background: #fff; padding: 20px 20px 0px 20px; .line { border-bottom: 1px solid #ddd; margin-bottom: 20px; } } .bottom { margin-top: 10px; height: 220px; background: #fff; padding: 20px 20px 0px 20px; overflow: auto; } } .right-card { background: #fff; border-radius: 4px; // padding: 0 20px 20px; padding: 0 20px 5px; width: 350px; box-sizing: border-box; .flow-chart { overflow: auto; height: calc(100vh - 200px); padding: 0; margin: 0; li { margin: 0; padding: 0 0 20px; list-style: none; display: flex; justify-content: space-between; position: relative; .right-conetnt { flex: 1; .name { font-size: 12px; color: #39c55a; margin-bottom: 10px; span { color: #999; } } .time { float: right; } .remark { padding: 10px; color: #666666; font-size: 12px; background: #f1f1f1; border-radius: 2px; .label { color: #999; margin-bottom: 10px; } } } .left-icon { width: 40px; height: 40px; text-align: center; line-height: 40px; background: #0084ff; border-radius: 10px; color: #fff; font-size: 20px; position: relative; margin-right: 27px; z-index: 2; .right-btm-status { position: absolute; bottom: 0px; right: -10px; height: 20px; width: 20px; line-height: 16px; border-radius: 10px; background: #39c55a; border: 2px solid #fff; font-size: 12px; box-sizing: border-box; } } } li::before { content: ""; position: absolute; top: 0; left: 20px; width: 2px; height: 100%; background: #ddd; z-index: 1; } li:last-child::before { display: none; } .flow-orange { .right-btm-status { background: #ff9a00 !important; } .name { color: #ff9a00 !important; } .left-icon { background: #ff9a00 !important; } } .flow-grey { .right-btm-status { background: #999 !important; } .name { color: #999 !important; } .left-icon { background: #999 !important; } } .flow-red { .right-btm-status { background: #ff4d4f !important; } .name { color: #ff4d4f !important; } .left-icon { background: #ff4d4f !important; } } } } } :deep(.el-upload-dragger .el-icon--upload) { margin-bottom: 0px; font-size: 40px; } :deep(.el-upload-dragger) { padding: 0px !important; } </style>