chenhj
2025-08-08 ab6f485ee5e2f7b49ad2ba91975f25a70144529a
src/views/collaborativeApproval/approvalProcess/components/approvalDia.vue
@@ -1,56 +1,157 @@
<template>
  <div>
    <el-dialog
        v-model="dialogFormVisible"
        :title="operationType === 'add' ? '新增审批流程' : '编辑审批流程'"
        width="700px"
        @close="closeDia"
      v-model="dialogFormVisible"
      :title="operationType === 'add' ? '新增审批流程' : '编辑审批流程'"
      width="700px"
      @close="closeDia"
    >
         <el-form :model="form" label-width="140px" label-position="top" ref="formRef">
            <el-row>
               <el-col :span="24">
                  <el-form-item label="流程编号:" prop="approveId">
                     <el-input v-model="form.approveId" placeholder="自动编号" clearable disabled/>
                  </el-form-item>
               </el-col>
            </el-row>
            <el-row>
               <el-col :span="24">
                  <el-form-item label="申请部门:" prop="approveDeptId">
                     <el-select
                        disabled
                        v-model="form.approveDeptId"
                        placeholder="选择部门"
                     >
                        <el-option
                           v-for="user in productOptions"
                           :key="user.deptId"
                           :label="user.deptName"
                           :value="user.deptId"
                        />
                     </el-select>
                  </el-form-item>
               </el-col>
            </el-row>
            <el-row>
               <el-col :span="24">
                  <el-form-item label="审批事由:" prop="approveReason">
                     <el-input v-model="form.approveReason" placeholder="请输入" clearable type="textarea" disabled/>
                  </el-form-item>
               </el-col>
            </el-row>
            <!-- 审批人选择(动态节点) -->
            <el-row :gutter="30">
               <el-col :span="12">
                  <el-form-item label="申请人:" prop="approveUser">
                     <el-select
                        v-model="form.approveUser"
                        placeholder="选择人员"
                        disabled
                     >
                        <el-option
                           v-for="user in userList"
                           :key="user.userId"
                           :label="user.nickName"
                           :value="user.userId"
                        />
                     </el-select>
                  </el-form-item>
               </el-col>
               <el-col :span="12">
                  <el-form-item label="申请日期:" prop="approveTime">
                     <el-date-picker
                        v-model="form.approveTime"
                        type="date"
                        placeholder="请选择日期"
                        value-format="YYYY-MM-DD"
                        format="YYYY-MM-DD"
                        clearable
                        style="width: 100%"
                        disabled
                     />
                  </el-form-item>
               </el-col>
            </el-row>
         </el-form>
      <el-form :model="{ activities }" ref="formRef" label-position="top">
        <el-timeline style="max-width: 600px">
          <el-timeline-item
              v-for="(activity, index) in activities"
              :key="index"
              :type="activity.current ? 'primary' : ''"
              :hollow="activity.current"
              :timestamp="activity.approveNodeTime"
        <el-steps :active="getActiveStep()" finish-status="success" process-status="process" align-center direction="vertical">
          <el-step
            v-for="(activity, index) in activities"
            :key="index"
                  finish-status="success"
            :title="getNodeTitle(index, activities.length)"
            :description="activity.approveNodeUser"
            :icon="getNodeIcon(activity, index)"
          >
            <el-card>
              <span style="font-size: 18px;font-weight: 700">节点{{activity.approveNodeOrder}}</span>
              <div style="margin: 10px 0">
                <span style="font-size: 16px;font-weight: 600">审批人:{{activity.approveNodeUser}}</span>
                  <template #icon>
                     <el-icon v-if="activity.approveNodeStatus === 2" color="red" :size="22"><WarningFilled /></el-icon>
                     <el-icon v-else-if="activity.isShen" color="#1890ff" :size="22"><Edit /></el-icon>
                     <el-icon v-else-if="activity.approveNodeStatus === 1" color="#67C23A" :size="26"><Check /></el-icon>
                     <el-icon v-else color="#C0C4CC" :size="22"><MoreFilled /></el-icon>
                  </template>
            <template #title>
              <span style="color: #000000">{{ getNodeTitle(index, activities.length) }}</span>
            </template>
            <template #description>
              <div class="node-user">
                <div class="avatar-wrapper">
                  <img :src="userStore.avatar" class="user-avatar" alt=""/>
                </div>
                <span style="color: #000000">{{ activity.approveNodeUser }}-{{activity.isApproval}}</span>
              </div>
              <div>
                <span style="margin-bottom: 8px;display: inline-block;font-size: 16px;font-weight: 600">审批意见:</span>
              <div v-if="!activity.isShen" class="node-reason">
                <span>审批意见:</span>{{ activity.approveNodeReason }}
              </div>
              <div v-else-if="activity.isShen">
                <el-form-item
                  v-if="activity.approveNodeRemark == 1"
                  :prop="'activities.' + index + '.approveNodeReason'"
                  :rules="[{ required: true, message: '审批意见不能为空', trigger: 'blur' }]"
                >
                  <el-input v-model="activity.approveNodeReason" clearable type="textarea" :disabled="operationType === 'view'"></el-input>
                </el-form-item>
                <el-form-item v-else>
                  <el-input v-model="activity.approveNodeReason" clearable type="textarea" disabled></el-input>
                </el-form-item>
              </div>
            </el-card>
          </el-timeline-item>
        </el-timeline>
            </template>
          </el-step>
        </el-steps>
      </el-form>
      <template #footer v-if="operationType === 'approval'">
        <div class="dialog-footer">
          <el-button type="primary" @click="submitForm(2)">不通过</el-button>
          <el-button type="primary" @click="submitForm(1)">通过</el-button>
          <el-button type="primary" @click="openSignatureDialog(2)">不通过</el-button>
          <el-button type="primary" @click="openSignatureDialog(1)">通过</el-button>
          <el-button @click="closeDia">取消</el-button>
        </div>
      </template>
    </el-dialog>
    <!-- 电子签名弹窗(vue3-signature-pad) -->
    <el-dialog v-model="signatureDialogVisible" title="电子签名" width="600px" append-to-body>
         <vueEsign
            ref="esign"
            class="mySign"
            :width="800"
            :height="300"
            :isCrop="isCrop"
            :lineWidth="lineWidth"
            :lineColor="lineColor"
         />
      <div style="margin-top:10px;">
        <el-button @click="clearSignature">清除</el-button>
        <el-button type="primary" @click="confirmSignature">确定</el-button>
      </div>
    </el-dialog>
  </div>
</template>
<script setup>
import {getCurrentInstance, ref} from "vue";
import {approveProcessDetails, updateApproveNode} from "../../../../api/collaborativeApproval/approvalProcess.js";
import { getCurrentInstance, reactive, ref, toRefs } from "vue";
import vueEsign from "vue-esign";
import {
   approveProcessDetails,
   getDept,
   updateApproveNode
} from "@/api/collaborativeApproval/approvalProcess.js";
import useUserStore from "@/store/modules/user.js";
import {userListNoPageByTenantId} from "@/api/system/user.js";
import { WarningFilled, Edit, Check, MoreFilled } from '@element-plus/icons-vue'
const emit = defineEmits(['close'])
const { proxy } = getCurrentInstance()
@@ -58,25 +159,111 @@
const operationType = ref('')
const activities = ref([])
const formRef = ref(null);
const userStore = useUserStore()
const productOptions = ref([]);
const userList = ref([])
const data = reactive({
   form: {
      approveTime: "",
      approveId: "",
      approveUser: "",
      approveDeptId: "",
      approveReason: "",
      checkResult: "",
   },
});
const { form } = toRefs(data);
const signatureDialogVisible = ref(false);
const signatureImg = ref('');
let submitStatus = null; // 临时存储通过/不通过状态
const isCrop = ref("");
const esign = ref(null);
const lineWidth = ref(0);
const lineColor = ref("#000000");
// 节点标题
const getNodeTitle = (index, len) => {
  if (index === len - 1) return '结束';
  return '审批';
};
// 获取当前激活步骤
const getActiveStep = () => {
  // 如果所有 isShen 都为 false,返回最后一个步骤(全部完成)
  const hasActive = activities.value.some(a => a.isShen === true);
  if (!hasActive) return activities.value.length;
  // 当前节点索引
  return activities.value.findIndex(a => a.isShen  == true);
};
// 步骤icon
const getNodeIcon = (activity, index) => {
  if (activity.approveNodeStatus === 2) return 'el-icon-warning'; // 不通过
  if (activity.isShen) return 'Edit';
  return '';
};
// 打开弹框
const openDialog = (type, row) => {
  operationType.value = type;
  dialogFormVisible.value = true;
   approveProcessDetails(row.approveId).then((res) => {
      console.log(res)
      activities.value = res.data
   })
   userListNoPageByTenantId().then((res) => {
      userList.value = res.data;
   });
   form.value = {...row}
   getProductOptions()
  approveProcessDetails(row.approveId).then((res) => {
    activities.value = res.data
    // 增加isApproval字段
    activities.value.forEach(item => {
      if (item.approveNodeStatus === 2) {
        item.isApproval = '已驳回';
      } else if (item.approveNodeStatus === 1) {
        item.isApproval = '已同意';
      } else {
        item.isApproval = '未审批';
      }
    })
  })
}
const getProductOptions = () => {
   getDept().then((res) => {
      productOptions.value = res.data;
   });
};
// 打开签名弹窗
const openSignatureDialog = (status) => {
  submitStatus = status;
  signatureDialogVisible.value = true;
};
// 清除签名
const clearSignature = () => {
   esign.value.reset();
};
// 确认签名
const confirmSignature = () => {
   esign.value.generate().then((res) => {
      console.log(res);
      signatureImg.value = res;
      signatureDialogVisible.value = false;
      clearSignature()
      submitForm(submitStatus);
   }).catch((err) => {
      console.log(err);
      proxy.$modal.msgWarning("请先签名!");
   })
};
// 提交审批
const submitForm = (status) => {
   const filteredActivities = activities.value.filter(activity => activity.approveNodeRemark == 1);
   filteredActivities[0].approveNodeStatus = status
   updateApproveNode(filteredActivities[0]).then(() => {
      proxy.$modal.msgSuccess("提交成功");
      closeDia();
   })
}
  const filteredActivities = activities.value.filter(activity => activity.isShen);
  filteredActivities[0].approveNodeStatus = status;
  filteredActivities[0].signatureImg = signatureImg.value; // 新增签名图片字段
  // 判断是否为最后一步
  const isLast = activities.value.findIndex(a => a.isShen) === activities.value.length-1;
  updateApproveNode({ ...filteredActivities[0], isLast, signatureImg: signatureImg.value }).then(() => {
    proxy.$modal.msgSuccess("提交成功");
    closeDia();
  });
};
// 关闭弹框
const closeDia = () => {
  proxy.resetForm("formRef");
@@ -89,7 +276,29 @@
</script>
<style scoped>
.el-timeline {
  padding-left: 10px;
.node-user {
  margin: 10px 0;
  font-size: 16px;
  font-weight: 600;
  display: flex;
  align-items: center;
  gap: 8px;
}
.node-status {
  color: #1890ff;
  margin-left: 8px;
  font-size: 14px;
}
.node-reason {
  font-size: 15px;
  color: #333;
  margin: 10px 0;
}
.user-avatar {
   cursor: pointer;
   width: 30px;
   height: 30px;
   border-radius: 50px;
}
</style>