Преглед изворни кода

feat(劳动合同): 新增解除/终止劳动合同审批详情页,并接入 hr_contract_termination_approval 流程组件映射

lihaiting пре 1 недеља
родитељ
комит
04b450e58a

+ 221 - 0
src/views/bpm/handleTask/components/hrRemoveContractManagement/hrRemoveContractDetail.vue

@@ -0,0 +1,221 @@
+<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">{{ avatarText }}</span>
+        <div class="cm-employee-info">
+          <b
+            >员工姓名:{{ detailRow.employeeName || '-'
+            }}<span class="cm-employee-no"
+              >合同编号:{{ detailRow.contractNo || '-' }}</span
+            ></b
+          >
+        </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.terminationTypeText
+            }}</span>
+          </div>
+          <div class="cm-compare-item">
+            <span class="cm-compare-label">签署方式:</span
+            ><span class="cm-compare-value">{{
+              detailRow.signingMethodText
+            }}</span>
+          </div>
+          <div class="cm-compare-item">
+            <span class="cm-compare-label">原因编码:</span
+            ><span class="cm-compare-value">{{ detailRow.reasonCodeText }}</span>
+          </div>
+          <div class="cm-compare-item">
+            <span class="cm-compare-label">生效日期:</span
+            ><span class="cm-compare-value">{{
+              detailRow.effectiveDate || '-'
+            }}</span>
+          </div>
+          <div class="cm-compare-item">
+            <span class="cm-compare-label">最后工作日期:</span
+            ><span class="cm-compare-value">{{
+              detailRow.lastWorkingDate || '-'
+            }}</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>
+        <div v-if="detailRow.reason" class="cm-detail-remark">
+          <strong>原因说明:</strong>{{ detailRow.reason }}
+        </div>
+      </div>
+    </div>
+    <el-empty v-else-if="!loading" description="暂无解除 / 终止详情" />
+  </main>
+</template>
+
+<script>
+import { getContractById } from '@/api/hr';
+import { getByCode } from '@/api/system/dictionary-data';
+import { parseAttachmentIds } from '@/utils/attachment';
+
+/** 业务类型:接口参数为 TERMINATE / END(见劳动合同解除终止接口) */
+const TERMINATION_TYPE_LABELS = {
+  TERMINATE: '解除',
+  END: '终止'
+};
+
+/** 签署方式:ONLINE-审批待办附件盖章签字;OFFLINE-线下签署 */
+const SIGNING_METHOD_LABELS = {
+  ONLINE: '线上',
+  OFFLINE: '线下'
+};
+
+/** 原因编码兜底项:字典 hr_contract_terminate 拉取失败时使用 */
+const TERMINATE_REASON_FALLBACK = [
+  { value: 'MUTUAL', label: '协商一致解除' },
+  { value: 'EMPLOYEE', label: '员工提出解除' },
+  { value: 'MISCONDUCT', label: '严重违纪' },
+  { value: 'CONTRACT_EXPIRE', label: '合同到期终止' },
+  { value: 'OTHER', label: '其他' }
+];
+
+function formatDate(value) {
+  if (!value) return '';
+  return String(value).slice(0, 10);
+}
+
+/**
+ * 解除/终止单的附件:文档里 terminations[] 给的是 attachmentIds;
+ * 线下签署时创建入参传的是 offlineFileId,两种都兼容,避免附件丢失。
+ */
+function pickTerminationFileIds(record = {}) {
+  const ids = parseAttachmentIds(record);
+  if (!ids.length && record.offlineFileId) ids.push(String(record.offlineFileId));
+  return ids;
+}
+
+export default {
+  name: 'HrRemoveContractDetail',
+  props: {
+    businessId: {
+      default: ''
+    }
+  },
+  data() {
+    return {
+      loading: false,
+      detailRow: null,
+      terminationReasons: TERMINATE_REASON_FALLBACK
+    };
+  },
+  computed: {
+    avatarText() {
+      return this.detailRow?.employeeName
+        ? this.detailRow.employeeName.slice(0, 1)
+        : '?';
+    }
+  },
+  watch: {
+    businessId: {
+      immediate: true,
+      handler(id) {
+        if (id) this.getInfo(id);
+      }
+    }
+  },
+  methods: {
+    async getInfo(id) {
+      if (!id) return;
+      this.loading = true;
+      // 原因编码字典单独拉取,失败不影响详情展示
+      this.loadTerminationReasons();
+      try {
+        const detail = await getContractById(id);
+        this.detailRow = this.adaptTermination(detail || {});
+      } catch (e) {
+        this.detailRow = null;
+        this.$message.error(e.message || '解除/终止详情加载失败');
+      } finally {
+        this.loading = false;
+      }
+    },
+    /**
+     * 合同详情接口返回该合同全部解除/终止记录(按创建时间倒序)。
+     * 审批单对应的是「最近一次解除终止单」terminationId,取不到时退回最新一条。
+     */
+    adaptTermination(contract = {}) {
+      const list = Array.isArray(contract.terminations)
+        ? contract.terminations
+        : [];
+      const record =
+        list.find(
+          (item) => String(item.id) === String(contract.terminationId)
+        ) ||
+        list[0] ||
+        {};
+      return {
+        id: record.id ?? '',
+        employeeName: contract.employeeName || '',
+        contractNo: contract.contractNo || '',
+        terminationType: record.terminationType || '',
+        terminationTypeText:
+          TERMINATION_TYPE_LABELS[record.terminationType] ||
+          record.terminationType ||
+          '-',
+        signingMethod: record.signingMethod || '',
+        signingMethodText:
+          SIGNING_METHOD_LABELS[
+            String(record.signingMethod || '').toUpperCase()
+          ] ||
+          record.signingMethodName ||
+          record.signingMethod ||
+          '-',
+        reasonCode: record.reasonCode || '',
+        reasonCodeText: this.reasonLabel(record.reasonCode),
+        effectiveDate: formatDate(record.effectiveDate),
+        lastWorkingDate: formatDate(record.lastWorkingDate),
+        reason: record.reason || '',
+        attachmentFileIds: pickTerminationFileIds(record)
+      };
+    },
+    reasonLabel(code) {
+      if (!code) return '-';
+      const hit = this.terminationReasons.find((item) => item.value === code);
+      return hit ? hit.label : code;
+    },
+    /** 原因编码字典:与 HR 侧同源 hr_contract_terminate */
+    async loadTerminationReasons() {
+      try {
+        const res = await getByCode('hr_contract_terminate');
+        const list = (res?.data || [])
+          .map((item) => {
+            const entry = Object.entries(item || {})[0] || [];
+            return { value: entry[0], label: entry[1] };
+          })
+          .filter((item) => item.value);
+        if (list.length) this.terminationReasons = list;
+      } catch (e) {
+        this.terminationReasons = TERMINATE_REASON_FALLBACK;
+      }
+    }
+  }
+};
+</script>
+
+<style lang="scss" scoped src="@/styles/views/hrContractManagement/index.scss"></style>

+ 139 - 0
src/views/bpm/handleTask/components/hrRemoveContractManagement/submit.vue

@@ -0,0 +1,139 @@
+<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'
+        }"
+      >
+        <ApprovalOpinion ref="approvalOpinion" v-model="form.reason" />
+      </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="$refs.approvalOpinion.validate(1) && handleAudit(1)"
+        >通过
+      </el-button>
+
+      <el-button
+        icon="el-icon-circle-close"
+        type="danger"
+        size="mini"
+        :loading="isLoading"
+        v-click-once
+        @click="$refs.approvalOpinion.validate(0) && handleAudit(0)"
+        >驳回
+      </el-button>
+      <el-dropdown
+        @command="(command) => handleCommand(command)"
+        style="margin-left: 30px"
+      >
+        <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 {
+    approveTaskWithVariables,
+    rejectTask,
+    cancelTask
+  } from '@/api/bpm/task';
+
+  export default {
+    name: 'HrContractSubmit',
+    props: {
+      businessId: {
+        default: ''
+      },
+      taskId: {
+        default: ''
+      },
+      id: {
+        default: ''
+      },
+      taskDefinitionKey: {
+        default: ''
+      }
+    },
+    data() {
+      return {
+        isLoading: false,
+        form: {
+          reason: '同意'
+        }
+      };
+    },
+    methods: {
+      async handleAudit(status) {
+        if (!this.form.reason && !!status) {
+          this.$message.warning('请填写审批意见');
+          return;
+        }
+        await this._approveTaskWithVariables(status);
+      },
+      handleCommand(command) {
+        if (command === 'cancel') {
+          this.$confirm('是否确认作废?', {
+            type: 'warning',
+            cancelButtonText: '取消',
+            confirmButtonText: '确定'
+          })
+            .then(() => {
+              cancelTask({
+                id: this.id,
+                taskId: this.taskId,
+                reason: this.form.reason,
+                businessId: this.businessId
+              })
+                .then(() => {
+                  this.$emit('handleClose');
+                })
+                .catch(() => {
+                  this.$message.error('流程作废失败');
+                });
+            })
+            .catch(() => {});
+        }
+      },
+      async _approveTaskWithVariables(status) {
+        const API = !!status ? approveTaskWithVariables : rejectTask;
+        try {
+          this.isLoading = true;
+          const res = await API({
+            id: this.taskId,
+            reason: this.form.reason,
+            variables: {
+              pass: !!status
+            }
+          });
+          if (res.data.code != '-1') {
+            this.$emit('handleAudit', {
+              status,
+              title: status === 0 ? '驳回' : ''
+            });
+          }
+        } finally {
+          this.isLoading = false;
+        }
+      }
+    }
+  };
+</script>
+
+<style lang="scss"></style>

+ 7 - 0
src/views/bpm/handleTask/index.vue

@@ -518,6 +518,13 @@
                 pcHandle:
                   '/bpm/handleTask/components/hrContractManagement/submit.vue'
               }
+            : businessKey === 'hr_contract_termination_approval'
+            ? {
+                pcView:
+                  '/bpm/handleTask/components/hrRemoveContractManagement/hrRemoveContractDetail.vue',
+                pcHandle:
+                  '/bpm/handleTask/components/hrRemoveContractManagement/submit.vue'
+              }
             : {};
         return {
           view: