Bläddra i källkod

合同签署详情页面

ZGC4846 1 månad sedan
förälder
incheckning
f728c45eb7

+ 128 - 0
src/api/hr/contractStatus.js

@@ -0,0 +1,128 @@
+/** 后端枚举码 → 中文;已是中文则原样返回 */
+function enumLabel(map, value, fallback = "-") {
+  if (value == null || value === "") return fallback;
+  const text = String(value).trim();
+  if (Object.prototype.hasOwnProperty.call(map, text)) return map[text];
+  const upper = text.toUpperCase();
+  if (Object.prototype.hasOwnProperty.call(map, upper)) return map[upper];
+  return text;
+}
+
+function toOptions(map, exclude = []) {
+  const skip = new Set(exclude);
+  return Object.keys(map)
+    .filter((key) => !skip.has(key) && key === key.toUpperCase() && !key.includes("_LEGACY"))
+    .map((value) => ({ value, label: map[value] }));
+}
+
+/**
+ * 流程节点内电子签章状态
+ * 线上签章由 Flowable 节点完成
+ */
+export const CONTRACT_FLOW_SIGN_STATUS_LABELS = {
+  NOT_STARTED: "未开始",
+  COMPLETED: "已完成",
+  CANCELLED: "已取消",
+};
+
+/**
+ * 签署状态
+ * 线上签章由 Flowable 节点完成
+ */
+export const CONTRACT_SIGNING_STATUS_LABELS = {
+  NOT_STARTED: "未签署",
+  COMPLETED: "已签署",
+  REJECTED: "已拒签",
+  EXPIRED: "已过期",
+  CANCELLED: "已取消",
+  // 历史数据兼容
+  UNSIGNED: "未签署",
+  PENDING: "待签署",
+  PENDING_SIGN: "待签署",
+  SIGNING: "签署中",
+  SIGNED: "已签署",
+};
+
+/**
+ * 合同状态
+ * SIGNING 仅兼容历史数据,线上签章不再进入该状态
+ */
+export const CONTRACT_STATUS_LABELS = {
+  DRAFT: "草稿",
+  PENDING_APPROVAL: "审批中",
+  PENDING_SIGN: "待签署",
+  SIGNED_WAIT_EFFECTIVE: "已签待生效",
+  EFFECTIVE: "生效",
+  EXPIRING: "临期",
+  EXPIRED: "已到期",
+  RENEWED: "已续签",
+  CHANGED: "已变更",
+  TERMINATED: "已解除",
+  ENDED: "已终止",
+  CANCELLED: "已作废",
+  SIGNING: "签署中",
+  // 历史数据兼容
+  PENDING_EFFECTIVE: "已签待生效",
+  VOIDED: "已作废",
+  ARCHIVED: "已归档",
+};
+
+export const CONTRACT_FLOW_SIGN_STATUS_OPTIONS = toOptions(
+  CONTRACT_FLOW_SIGN_STATUS_LABELS,
+);
+
+export const CONTRACT_SIGNING_STATUS_OPTIONS = toOptions(
+  CONTRACT_SIGNING_STATUS_LABELS,
+  ["UNSIGNED", "PENDING", "PENDING_SIGN", "SIGNING", "SIGNED"],
+);
+
+/** 筛选下拉:不含仅用于历史兼容的 SIGNING */
+export const CONTRACT_STATUS_OPTIONS = toOptions(CONTRACT_STATUS_LABELS, [
+  "SIGNING",
+  "PENDING_EFFECTIVE",
+  "VOIDED",
+  "ARCHIVED",
+]);
+
+export function contractFlowSignStatusLabel(value, fallback = "-") {
+  return enumLabel(CONTRACT_FLOW_SIGN_STATUS_LABELS, value, fallback);
+}
+
+export function contractSigningStatusLabel(value, fallback = "-") {
+  return enumLabel(CONTRACT_SIGNING_STATUS_LABELS, value, fallback);
+}
+
+export function contractStatusLabel(value, fallback = "") {
+  return enumLabel(CONTRACT_STATUS_LABELS, value, fallback);
+}
+
+export function pickFlowSignStatus(record = {}) {
+  return (
+    record.flowSignStatus ||
+    record.flowNodeSignStatus ||
+    record.nodeEsignStatus ||
+    record.esignStatus ||
+    record.eSignStatus ||
+    record.electronicSignStatus ||
+    ""
+  );
+}
+
+export function adaptContractFlowSignNodes(record = {}) {
+  const list =
+    record.flowSignNodes ||
+    record.signNodes ||
+    record.esignNodes ||
+    record.flowNodes ||
+    [];
+  if (!Array.isArray(list)) return [];
+  return list.map((item = {}) => {
+    const status =
+      item.status || item.signStatus || item.esignStatus || item.eSignStatus || "";
+    return {
+      ...item,
+      status,
+      statusLabel: contractFlowSignStatusLabel(status),
+    };
+  });
+}

+ 14 - 0
src/api/hr/index.js

@@ -929,6 +929,20 @@ export const getContractTemplateVersions = async (id) =>
 export const createContractTemplateVersion = async (id, data) =>
   unwrap(await request.post(`/hr/contracts/templates/${id}/versions`, data));
 
+export {
+  CONTRACT_FLOW_SIGN_STATUS_LABELS,
+  CONTRACT_FLOW_SIGN_STATUS_OPTIONS,
+  CONTRACT_SIGNING_STATUS_LABELS,
+  CONTRACT_SIGNING_STATUS_OPTIONS,
+  CONTRACT_STATUS_LABELS,
+  CONTRACT_STATUS_OPTIONS,
+  adaptContractFlowSignNodes,
+  contractFlowSignStatusLabel,
+  contractSigningStatusLabel,
+  contractStatusLabel,
+  pickFlowSignStatus,
+} from "./contractStatus";
+
 // 劳动合同管理
 export const getContractPage = async (data = {}) =>
   unwrap(await request.get("/hr/contracts", { params: compact(data) }));

+ 254 - 0
src/styles/views/hrContractManagement/index.scss

@@ -0,0 +1,254 @@
+.cm-detail-host,
+.cm-detail-host * {
+  box-sizing: border-box;
+}
+
+.cm-detail-host {
+  min-height: 100%;
+  padding: 16px 18px 22px;
+  color: #22334a;
+  background: #f3f6fb;
+}
+
+.cm-create-page {
+  padding: 0;
+}
+
+.cm-create-section {
+  padding: 12px 22px 8px;
+  background: #fff;
+  border-bottom: 1px solid #edf1f6;
+}
+
+.cm-create-section-title {
+  font-size: 14px;
+  color: #243955;
+  font-weight: 600;
+  margin-bottom: 8px;
+  display: flex;
+  align-items: center;
+  gap: 6px;
+}
+
+.cm-create-section-title::before {
+  content: "";
+  display: inline-block;
+  width: 3px;
+  height: 14px;
+  background: #1768e5;
+  border-radius: 1px;
+}
+
+.cm-create-employee-card {
+  background: #f8fbff;
+  border: 1px solid #dbe7ff;
+  border-radius: 6px;
+  padding: 12px 16px;
+  margin-bottom: 14px;
+  display: flex;
+  align-items: center;
+  gap: 16px;
+  flex-wrap: wrap;
+}
+
+.cm-create-employee-card .cm-employee-avatar {
+  width: 42px;
+  height: 42px;
+  display: grid;
+  place-items: center;
+  border-radius: 50%;
+  background: linear-gradient(135deg, #1768e5, #4c8be8);
+  color: #fff;
+  font-size: 18px;
+  font-weight: 600;
+}
+
+.cm-create-employee-card .cm-employee-info {
+  display: flex;
+  flex-direction: column;
+  gap: 2px;
+}
+
+.cm-create-employee-card .cm-employee-info b {
+  color: #243955;
+  font-size: 14px;
+}
+
+.cm-create-grid {
+  display: grid;
+  grid-template-columns: 1fr 1fr;
+  gap: 4px 20px;
+}
+
+.cm-compare-item {
+  display: flex;
+  flex-direction: row;
+  align-items: baseline;
+  gap: 0;
+}
+
+.cm-compare-item.is-span-2 {
+  grid-column: span 2;
+}
+
+.cm-compare-item .cm-compare-label {
+  flex: none;
+  color: #7b899b;
+  font-size: 13px;
+  line-height: 20px;
+}
+
+.cm-compare-item .cm-compare-value {
+  min-width: 0;
+  color: #243955;
+  font-size: 14px;
+  line-height: 20px;
+  font-weight: 500;
+  word-break: break-word;
+}
+
+.cm-compare-item .cm-compare-value.is-highlight {
+  color: #1768e5;
+  font-weight: 600;
+}
+
+.cm-detail-page,
+.cm-detail-page * {
+  box-sizing: border-box;
+}
+
+.cm-detail-page {
+  padding: 0;
+  background: #fff;
+  border: 1px solid #e0e7f1;
+  border-radius: 12px;
+  overflow: hidden;
+}
+
+.cm-detail-page .cm-create-section {
+  padding: 18px 26px 14px;
+}
+
+.cm-detail-page .cm-create-section-title {
+  margin-bottom: 14px;
+  font-size: 15px;
+}
+
+.cm-detail-page .cm-create-section-title::before {
+  height: 15px;
+}
+
+.cm-detail-page .cm-detail-employee {
+  margin: 16px 26px 0;
+  padding: 14px 18px;
+}
+
+.cm-detail-page .cm-create-employee-card .cm-employee-avatar {
+  width: 48px;
+  height: 48px;
+  font-size: 20px;
+}
+
+.cm-detail-page .cm-create-employee-card .cm-employee-info {
+  gap: 6px;
+}
+
+.cm-detail-page .cm-create-employee-card .cm-employee-info b {
+  font-size: 16px;
+}
+
+.cm-detail-page .cm-employee-no {
+  margin-left: 24px;
+}
+
+.cm-detail-page .cm-detail-status {
+  margin-left: auto;
+  text-align: right;
+}
+
+.cm-detail-page .cm-detail-status small {
+  display: block;
+  margin-bottom: 6px;
+  color: #7e8b9d;
+  font-size: 13px;
+}
+
+.cm-detail-page .cm-create-grid {
+  gap: 16px 26px;
+}
+
+.cm-detail-page .cm-detail-muted {
+  color: #9aa5b3;
+}
+
+.cm-detail-page .cm-detail-remark {
+  margin-top: 16px;
+  padding: 12px 14px;
+  border-left: 3px solid #e6a23c;
+  border-radius: 3px;
+  background: #fff7e6;
+  color: #7a5c00;
+  font-size: 13px;
+  line-height: 1.8;
+}
+
+.cm-detail-page .cm-detail-remark strong {
+  color: #8a6100;
+}
+
+.cm-detail-page .cm-status {
+  display: inline-block;
+  padding: 3px 12px;
+  border-radius: 12px;
+  font-size: 13px;
+}
+
+.cm-detail-page .cm-status.draft {
+  color: #5a6a7c;
+  background: #eef1f5;
+}
+
+.cm-detail-page .cm-status.pending {
+  color: #c67d23;
+  background: #fff3e2;
+}
+
+.cm-detail-page .cm-status.trial {
+  color: #1768e5;
+  background: #eaf2ff;
+}
+
+.cm-detail-page .cm-status.expired {
+  color: #d55752;
+  background: #ffe9e8;
+}
+
+.cm-detail-page .cm-status.terminated {
+  color: #7e8b9d;
+  background: #eef0f4;
+}
+
+.cm-detail-page .cm-status.changed {
+  color: #7553be;
+  background: #f0ebfb;
+}
+
+.cm-detail-page .cm-status.archived {
+  color: #5a6a7c;
+  background: #e8edf3;
+}
+
+.cm-detail-page .cm-status.expiring {
+  color: #db7a25;
+  background: #fff1e5;
+}
+
+.cm-detail-page .cm-status.signing {
+  color: #1768e5;
+  background: #eaf2ff;
+}
+
+.cm-detail-page .cm-status.active {
+  color: #168d69;
+  background: #e7f7f1;
+}

+ 342 - 0
src/views/bpm/handleTask/components/hrContractManagement/hrContractDetail.vue

@@ -0,0 +1,342 @@
+<template>
+  <main class="cm-detail-host" v-loading="loading">
+    <div v-if="detailRow" class="cm-create-page cm-detail-page">
+      <div class="cm-create-employee-card cm-detail-employee">
+        <span class="cm-employee-avatar">{{
+          detailRow.employeeName ? detailRow.employeeName.slice(0, 1) : '?'
+        }}</span>
+        <div class="cm-employee-info">
+          <b
+            >员工姓名:{{ detailRow.employeeName
+            }}<span class="cm-employee-no"
+              >员工工号:{{ detailRow.employeeNo }}</span
+            ></b
+          >
+        </div>
+        <div class="cm-detail-status">
+          <small>合同状态</small>
+          <span class="cm-status" :class="statusClass(detailRow.status)">{{
+            detailRow.status
+          }}</span>
+        </div>
+      </div>
+
+      <div class="cm-create-section">
+        <div class="cm-create-section-title">合同基本信息</div>
+        <div class="cm-create-grid">
+          <div class="cm-compare-item">
+            <span class="cm-compare-label">合同编号:</span
+            ><span class="cm-compare-value">{{ detailRow.contractNo }}</span>
+          </div>
+          <div class="cm-compare-item">
+            <span class="cm-compare-label">合同类型:</span
+            ><span class="cm-compare-value">{{ detailRow.contractType }}</span>
+          </div>
+          <div class="cm-compare-item">
+            <span class="cm-compare-label">所属机构:</span
+            ><span class="cm-compare-value">{{ detailRow.legalEntity }}</span>
+          </div>
+          <div class="cm-compare-item">
+            <span class="cm-compare-label">所属部门:</span
+            ><span class="cm-compare-value">{{ detailRow.department }}</span>
+          </div>
+          <div class="cm-compare-item">
+            <span class="cm-compare-label">岗位名称:</span
+            ><span class="cm-compare-value">{{ detailRow.positionName }}</span>
+          </div>
+          <div class="cm-compare-item">
+            <span class="cm-compare-label">岗位层级:</span
+            ><span class="cm-compare-value">{{
+              detailRow.level || '-'
+            }}</span>
+          </div>
+          <div class="cm-compare-item">
+            <span class="cm-compare-label">直属上级:</span
+            ><span class="cm-compare-value">{{
+              detailRow.manager || '-'
+            }}</span>
+          </div>
+        </div>
+      </div>
+
+      <div class="cm-create-section">
+        <div class="cm-create-section-title">合同期限与薪资</div>
+        <div class="cm-create-grid">
+          <div class="cm-compare-item">
+            <span class="cm-compare-label">合同起始:</span
+            ><span class="cm-compare-value">{{
+              detailRow.startDate || '-'
+            }}</span>
+          </div>
+          <div class="cm-compare-item">
+            <span class="cm-compare-label">合同终止:</span
+            ><span class="cm-compare-value">{{
+              detailRow.endDate || '长期'
+            }}</span>
+          </div>
+          <div class="cm-compare-item">
+            <span class="cm-compare-label">月薪资:</span
+            ><span class="cm-compare-value is-highlight">{{
+              detailRow.salary
+                ? '¥ ' + Number(detailRow.salary).toLocaleString() + ' 元/月'
+                : '待定'
+            }}</span>
+          </div>
+          <div class="cm-compare-item">
+            <span class="cm-compare-label">试用期至:</span
+            ><span class="cm-compare-value">{{
+              detailRow.probationEnd || '无试用期'
+            }}</span>
+          </div>
+        </div>
+      </div>
+
+      <div class="cm-create-section">
+        <div class="cm-create-section-title">签署信息</div>
+        <div class="cm-create-grid">
+          <div class="cm-compare-item">
+            <span class="cm-compare-label">签署方式:</span
+            ><span class="cm-compare-value">{{
+              detailRow.signMethod || '-'
+            }}</span>
+          </div>
+          <div class="cm-compare-item">
+            <span class="cm-compare-label">签署状态:</span
+            ><span class="cm-compare-value">{{
+              detailRow.signingStatusLabel || '-'
+            }}</span>
+          </div>
+          <div
+            v-if="detailRow.flowSignStatus"
+            class="cm-compare-item"
+          >
+            <span class="cm-compare-label">流程节点电子签章:</span
+            ><span class="cm-compare-value">{{
+              detailRow.flowSignStatusLabel || '-'
+            }}</span>
+          </div>
+          <div
+            class="cm-compare-item is-span-2"
+            v-if="detailRow.flowSignNodes && detailRow.flowSignNodes.length"
+          >
+            <span class="cm-compare-label">签章节点:</span>
+            <span class="cm-compare-value">
+              {{
+                detailRow.flowSignNodes
+                  .map(
+                    (n) =>
+                      (n.name || n.nodeName || n.taskName || '节点') +
+                      '(' +
+                      (n.statusLabel || '-') +
+                      ')'
+                  )
+                  .join('、')
+              }}
+            </span>
+          </div>
+          <div class="cm-compare-item">
+            <span class="cm-compare-label">合同附件:</span
+            ><span class="cm-compare-value">
+              <fileMain
+                v-if="
+                  detailRow.attachmentFileIds &&
+                  detailRow.attachmentFileIds.length
+                "
+                :value="detailRow.attachmentFileIds"
+                type="view"
+                size="small"
+              />
+              <span v-else class="cm-detail-muted">暂无附件</span>
+            </span>
+          </div>
+          <div class="cm-compare-item">
+            <span class="cm-compare-label">签订日期:</span
+            ><span class="cm-compare-value">{{
+              detailRow.signDate || detailRow.createdAt || '-'
+            }}</span>
+          </div>
+          <div class="cm-compare-item">
+            <span class="cm-compare-label">创建时间:</span
+            ><span class="cm-compare-value">{{
+              detailRow.createdAt || '-'
+            }}</span>
+          </div>
+        </div>
+        <div v-if="detailRow.remark" class="cm-detail-remark">
+          <strong>备注:</strong>{{ detailRow.remark }}
+        </div>
+      </div>
+    </div>
+    <el-empty v-else-if="!loading" description="暂无合同详情" />
+  </main>
+</template>
+
+<script>
+import {
+  getContractById,
+  adaptContractFlowSignNodes,
+  contractSigningStatusLabel,
+  contractStatusLabel,
+  contractFlowSignStatusLabel,
+  pickFlowSignStatus
+} from '@/api/hr';
+
+const CONTRACT_TYPES = [
+  { value: 'FIXED', label: '固定期限劳动合同' },
+  { value: 'NON_FIXED', label: '无固定期限劳动合同' },
+  { value: 'TASK', label: '以完成一定任务为期限' },
+  { value: 'DISPATCH', label: '劳务派遣合同' },
+  { value: 'INTERN', label: '实习协议' },
+  { value: 'CONFIDENTIAL', label: '保密协议' },
+  { value: 'NON_COMPETE', label: '竞业限制协议' },
+  { value: 'CHANGE', label: '合同变更协议' },
+  { value: 'TERMINATION', label: '合同终止协议' }
+];
+
+function formatDate(value) {
+  if (!value) return '';
+  return String(value).slice(0, 10);
+}
+
+function formatDateTime(value) {
+  if (!value) return '';
+  return String(value).replace('T', ' ').slice(0, 16);
+}
+
+function typeLabel(value) {
+  return CONTRACT_TYPES.find((t) => t.value === value)?.label || value || '';
+}
+
+function signMethodLabel(value) {
+  if (value === 'ONLINE') return '线上';
+  if (value === 'OFFLINE') return '线下';
+  return value || '';
+}
+
+function collectAttachmentFileIds(files) {
+  const list = (Array.isArray(files) ? files : []).filter(Boolean);
+  const attach = list.filter((item) =>
+    ['ATTACHMENT', 'SCAN', 'ARCHIVE'].includes(item.filePurpose)
+  );
+  const source = attach.length ? attach : list;
+  return source
+    .map((item) => item.fileId || item.id)
+    .filter((id) => id != null && id !== '');
+}
+
+function adaptContract(item = {}) {
+  const files = item.files || [];
+  const startDate = formatDate(item.startDate);
+  const endDate = formatDate(item.endDate);
+  const termText =
+    item.contractTerm ||
+    (item.contractTermMonths != null && item.contractTermMonths !== ''
+      ? `${item.contractTermMonths}个月`
+      : '');
+  return {
+    ...item,
+    employeeNo: item.employeeCode || item.employeeNo || '',
+    employeeCode: item.employeeCode || item.employeeNo || '',
+    legalEntity: item.legalEntityName || '',
+    department: item.departmentName || '',
+    contractTypeCode: item.contractType || '',
+    contractType: typeLabel(item.contractType),
+    signMethod: signMethodLabel(item.signingMethod),
+    signingStatus: item.signingStatus || '',
+    signingStatusLabel: contractSigningStatusLabel(item.signingStatus),
+    flowSignStatus: pickFlowSignStatus(item),
+    flowSignStatusLabel: contractFlowSignStatusLabel(pickFlowSignStatus(item)),
+    flowSignNodes: adaptContractFlowSignNodes(item),
+    status: contractStatusLabel(item.contractStatus),
+    statusCode: item.contractStatus || '',
+    salary: item.salaryStandard,
+    signDate: formatDate(item.signingDate),
+    startDate,
+    endDate,
+    contractTerm: termText,
+    remainingDays:
+      item.remainingDays === null ||
+      item.remainingDays === undefined ||
+      item.remainingDays === ''
+        ? null
+        : Number(item.remainingDays),
+    probationMonths: item.probationMonths,
+    createdAt: formatDateTime(item.createTime),
+    probationEnd: formatDate(item.probationEndDate),
+    attachmentFileIds: collectAttachmentFileIds(files),
+    level: item.positionLevel || item.level || '',
+    manager: item.managerName || item.manager || '',
+    archived:
+      item.contractStatus === 'ARCHIVED' ||
+      item.archived === true ||
+      item.archived === 1
+  };
+}
+
+export default {
+  name: 'HrContractDetail',
+  props: {
+    businessId: {
+      default: ''
+    }
+  },
+  data() {
+    return {
+      loading: false,
+      detailRow: null
+    };
+  },
+  watch: {
+    businessId: {
+      immediate: true,
+      handler(id) {
+        if (id) this.getInfo(id);
+      }
+    }
+  },
+  methods: {
+    async getInfo(id) {
+      if (!id) return;
+      this.loading = true;
+      try {
+        const detail = await getContractById(id);
+        this.detailRow = adaptContract(detail || {});
+      } catch (e) {
+        this.detailRow = null;
+        this.$message.error(e.message || '合同详情加载失败');
+      } finally {
+        this.loading = false;
+      }
+    },
+    statusClass(s) {
+      const label = contractStatusLabel(s, s);
+      const map = {
+        草稿: 'draft',
+        审批中: 'pending',
+        待签署: 'pending',
+        待签: 'pending',
+        签署中: 'signing',
+        已签待生效: 'pending',
+        待生效: 'pending',
+        生效: 'active',
+        生效中: 'active',
+        临期: 'expiring',
+        即将到期: 'expiring',
+        已变更: 'changed',
+        已续签: 'active',
+        已解除: 'terminated',
+        已终止: 'terminated',
+        已作废: 'expired',
+        已取消: 'expired',
+        已归档: 'archived',
+        试用期: 'trial',
+        已到期: 'expired',
+        已过期: 'expired'
+      };
+      return map[label] || map[s] || 'pending';
+    }
+  }
+};
+</script>
+
+<style lang="scss" scoped src="@/styles/views/hrContractManagement/index.scss"></style>

+ 207 - 0
src/views/bpm/handleTask/components/hrContractManagement/submit.vue

@@ -0,0 +1,207 @@
+<template>
+  <el-col :span="16" :offset="6">
+    <el-form label-width="100px" ref="formRef" :model="form">
+      <el-form-item
+        label="审批建议"
+        style="margin-bottom: 20px"
+        :rules="{
+          required: true,
+          message: '请选择',
+          trigger: 'change'
+        }"
+      >
+        <el-input
+          type="textarea"
+          v-model="form.reason"
+          placeholder="请输入审批建议"
+        />
+      </el-form-item>
+    </el-form>
+    <div style="margin-left: 10%; margin-bottom: 20px; font-size: 14px">
+      <el-button
+        icon="el-icon-edit-outline"
+        type="success"
+        size="mini"
+        :loading="isLoading"
+        v-click-once
+        @click="handleAudit(1)"
+        >通过
+      </el-button>
+      <el-button
+        icon="el-icon-circle-close"
+        type="danger"
+        size="mini"
+        v-click-once
+        @click="handleAudit(0)"
+        >驳回
+      </el-button>
+
+      <el-dropdown
+        @command="(command) => handleCommand(command)"
+        style="margin-left: 30px"
+        v-if="taskDefinitionKey != 'deptLeaderApprove'"
+      >
+        <span class="el-dropdown-link"
+          >更多<i class="el-icon-arrow-down el-icon--right"></i
+        ></span>
+        <el-dropdown-menu slot="dropdown">
+          <el-dropdown-item command="cancel">作废</el-dropdown-item>
+        </el-dropdown-menu>
+      </el-dropdown>
+    </div>
+  </el-col>
+</template>
+
+<script>
+  // import outin from '@/api/warehouseManagement/outin';
+  import storageApi from '@/api/warehouseManagement';
+  import { cancel } from '@/api/bpm/components/purchasingManage/outSourceSend';
+  import {approveTaskWithVariables, rejectTask} from '@/api/bpm/task';
+
+  // 流程实例的详情页,可用于审批
+  export default {
+    name: '',
+    components: {
+      //   Parser
+    },
+    props: {
+      businessId: {
+        default: ''
+      },
+      taskId: {
+        default: ''
+      },
+      id: {
+        default: ''
+      },
+      taskDefinitionKey: {
+        default: ''
+      }
+    },
+    data() {
+      return {
+        isLoading: false,
+        form: {
+          reason: '同意'
+        }
+      };
+    },
+    methods: {
+      async handleAudit(status) {
+        // console.log(status);
+        // let storemanIds = '';
+        // let permissionType = this.tabOptions.find(
+        //   (item) => item.key == this.taskDefinitionKey
+        // )?.permissionType;
+        // if (this.taskDefinitionKey === 'deptLeaderApprove') {
+        //   let arr = await this.getTableValue();
+        //   let ids = arr.productList.map((item) => item.warehouseId);
+        //   let data = await getWarehouseListByIds(ids || []);
+        //   storemanIds = [...new Set(data.map((item) => item.ownerId))].join(
+        //     ','
+        //   );
+        // }
+        let data = await this.getTableValue();
+        let storageData = data.returnStorageData;
+        if (!!status) {
+          if (!storageData.extInfo.verifyDeptCode) {
+            this.$message.error('请选择部门');
+            return false;
+          }
+          if (!storageData.fromUser) {
+            this.$message.error('请选择领料人');
+            return false;
+          }
+        }
+        if (!this.form.reason && !!status) {
+          this.$message.error('请输入审批意见');
+          return false;
+        }
+        if (this.taskDefinitionKey === 'storeman' && !!status) {
+          // 入库来源isSkip 0-正常  1-外部(外部跳过内部审核流程)
+          storageData.isSkip = 1;
+          console.log(storageData);
+          try {
+            this.isLoading = true;
+            const res = await storageApi.outStorage(storageData);
+            if (res.code == 0) {
+              let API = !!status ? approveTaskWithVariables : rejectTask;
+              API({
+                id: this.taskId,
+                reason: this.form.reason,
+                variables: {
+                  pass: !!status
+                }
+              }).then((res) => {
+                if (res.code != '-1') {
+                  this.$emit('handleAudit', {
+                    status: status,
+                    title: !status ? '驳回' : ''
+                  });
+                }
+                this.isLoading = false;
+              });
+            }
+          } catch (error) {
+            this.isLoading = false;
+            console.error('保存失败:', error);
+          }
+        } else {
+          // await this._approveTaskWithVariables(status, storemanIds);
+          await this._approveTaskWithVariables(status);
+        }
+      },
+      getTableValue() {
+        return new Promise((resolve, reject) => {
+          this.$emit('getTableValue', async (data) => {
+            resolve(await data);
+          });
+        });
+      },
+      async _approveTaskWithVariables(status, storemanIds) {
+        let variables = {
+          pass: !!status
+        };
+        let API = !!status ? approveTaskWithVariables : rejectTask;
+        API({
+          id: this.taskId,
+          reason: this.form.reason,
+          variables
+        }).then((res) => {
+          if (res.data.code != '-1') {
+            this.$emit('handleAudit', {
+              status,
+              title: status === 0 ? '驳回' : ''
+            });
+          }
+        });
+      },
+      //更多
+      handleCommand(command) {
+        if (command === 'cancel') {
+          this.$confirm('是否确认作废?', {
+            type: 'warning',
+            cancelButtonText: '取消',
+            confirmButtonText: '确定'
+          })
+            .then(() => {
+              cancel({
+                id: this.taskId,
+                reason: this.form.reason,
+                businessId: this.businessId
+              })
+                .then(() => {
+                  this.$emit('handleClose');
+                })
+                .catch(() => {
+                  this.$message.error('流程作废失败');
+                });
+            })
+            .catch(() => {});
+        }
+      }
+    }
+  };
+</script>
+
+<style lang="scss"></style>

+ 11 - 4
src/views/bpm/handleTask/index.vue

@@ -487,14 +487,14 @@
           this.parseFormRouteConfig(this.listData.formKey) ||
           this.parseFormRouteConfig(this.listData.formCustomCreatePath) ||
           {};
-        // 录用审批:流程未配路由时使用本项目约定路径
+        // 录用审批 / 劳动合同:流程未配路由时使用本项目约定路径
         const businessKey = String(
           def.key ||
             this.listData.businessKey ||
             this.listData.processDefinitionKey ||
             ''
         );
-        const hiringFallback =
+        const formFallback =
           businessKey === 'hr_recruit_hiring_approval'
             ? {
                 pcView:
@@ -502,18 +502,25 @@
                 pcHandle:
                   '/bpm/handleTask/components/hiringApproval/submit.vue'
               }
+            : businessKey === 'hr_contract_approval'
+            ? {
+                pcView:
+                  '/bpm/handleTask/components/hrContractManagement/hrContractDetail.vue',
+                pcHandle:
+                  '/bpm/handleTask/components/hrContractManagement/submit.vue'
+              }
             : {};
         return {
           view:
             fromTodo.view ||
             this.normalizeViewPath(fromDef.pcView || fromDef.pcViewRouter) ||
-            this.normalizeViewPath(hiringFallback.pcView),
+            this.normalizeViewPath(formFallback.pcView),
           handle:
             fromTodo.handle ||
             this.normalizeViewPath(
               fromDef.pcHandle || fromDef.pcHandleRouter
             ) ||
-            this.normalizeViewPath(hiringFallback.pcHandle)
+            this.normalizeViewPath(formFallback.pcHandle)
         };
       },
       getBusinessComponentRouter(type) {

+ 2 - 2
vue.config.js

@@ -38,7 +38,7 @@ module.exports = {
         // target: 'http://124.71.68.31:50001', // 测试环境
         // target: 'http://124.71.68.31:50001',
         // target: 'http://192.168.1.105:18086',
-        target: 'http://localhost:18086',
+        // target: 'http://localhost:18086',
         // target: 'http://192.168.1.116:18086',
         // target: 'http://192.168.1.3:18086',
         // target: 'http://192.168.1.251:18186',
@@ -46,7 +46,7 @@ module.exports = {
         // target: 'http://192.168.1.3:18086',
         // target: 'http://aiot.zoomwin.com.cn:51001/api',
         // target: 'http://192.168.1.3:18086',
-        // target: 'http://192.168.1.147:18086', // 陈兵
+        target: 'http://192.168.1.147:18086', // 陈兵
         // target: 'http://192.168.1.116:18086', // 赵沙金
         // target: 'http://192.168.1.113:18086', // 郭丹