|
|
@@ -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>
|