|
@@ -220,7 +220,8 @@
|
|
|
</div>
|
|
</div>
|
|
|
<div class="cm-detail-status">
|
|
<div class="cm-detail-status">
|
|
|
<small>合同状态</small>
|
|
<small>合同状态</small>
|
|
|
- <span class="cm-status" :class="statusClass(detailRow.statusCode)">{{ detailRow.status }}</span>
|
|
|
|
|
|
|
+ <!-- 与列表「状态」列同一套 el-tag 配色(statusTagType),不再用自定义底色 -->
|
|
|
|
|
+ <el-tag size="mini" :type="statusTagType(detailRow.statusCode)">{{ detailRow.status }}</el-tag>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
@@ -310,9 +311,9 @@
|
|
|
<div v-for="change in detailChanges" :key="'change-' + change.id" class="cm-detail-flow">
|
|
<div v-for="change in detailChanges" :key="'change-' + change.id" class="cm-detail-flow">
|
|
|
<div class="cm-detail-flow-head">
|
|
<div class="cm-detail-flow-head">
|
|
|
<span class="cm-detail-flow-title">变更单 {{ change.changeNo || '(编号未返回)' }}</span>
|
|
<span class="cm-detail-flow-title">变更单 {{ change.changeNo || '(编号未返回)' }}</span>
|
|
|
- <el-tag size="mini" :type="changeStatusTagType(change.changeStatus)">{{ change.changeStatus || '状态未返回'
|
|
|
|
|
|
|
+ <el-tag size="mini" :type="changeStatusTagType(change.changeStatus)">{{ change.changeStatusText ||
|
|
|
|
|
+ '状态未返回'
|
|
|
}}</el-tag>
|
|
}}</el-tag>
|
|
|
- <span class="cm-detail-flow-meta">变更类型:{{ change.changeTypeText }}</span>
|
|
|
|
|
<span class="cm-detail-flow-meta">生效日期:{{ change.effectiveDateText }}</span>
|
|
<span class="cm-detail-flow-meta">生效日期:{{ change.effectiveDateText }}</span>
|
|
|
<span class="cm-detail-flow-meta">签署方式:{{ change.signingMethodText }}</span>
|
|
<span class="cm-detail-flow-meta">签署方式:{{ change.signingMethodText }}</span>
|
|
|
<span class="cm-detail-flow-meta">审批单据:{{ change.documentCode || '-' }}</span>
|
|
<span class="cm-detail-flow-meta">审批单据:{{ change.documentCode || '-' }}</span>
|
|
@@ -330,6 +331,14 @@
|
|
|
<span class="cm-compare-label">{{ field.label }}:</span>
|
|
<span class="cm-compare-label">{{ field.label }}:</span>
|
|
|
<span class="cm-compare-value">{{ compareFieldText(change.before, field) }}</span>
|
|
<span class="cm-compare-value">{{ compareFieldText(change.before, field) }}</span>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
+ <div class="cm-compare-item is-span-2">
|
|
|
|
|
+ <span class="cm-compare-label">合同附件:</span>
|
|
|
|
|
+ <span class="cm-compare-value">
|
|
|
|
|
+ <fileMain v-if="change.before && change.before.attachmentFileIds.length"
|
|
|
|
|
+ :value="change.before.attachmentFileIds" type="view" size="small" />
|
|
|
|
|
+ <span v-else class="cm-detail-muted">暂无附件</span>
|
|
|
|
|
+ </span>
|
|
|
|
|
+ </div>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
<div class="cm-compare-card">
|
|
<div class="cm-compare-card">
|
|
@@ -343,6 +352,14 @@
|
|
|
:class="['cm-compare-value', { 'is-highlight': compareFieldChanged(change.before, change.after, field) }]">{{
|
|
:class="['cm-compare-value', { 'is-highlight': compareFieldChanged(change.before, change.after, field) }]">{{
|
|
|
compareFieldText(change.after, field) }}</span>
|
|
compareFieldText(change.after, field) }}</span>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
+ <div class="cm-compare-item is-span-2">
|
|
|
|
|
+ <span class="cm-compare-label">合同附件:</span>
|
|
|
|
|
+ <span class="cm-compare-value">
|
|
|
|
|
+ <fileMain v-if="change.after && change.after.attachmentFileIds.length"
|
|
|
|
|
+ :value="change.after.attachmentFileIds" type="view" size="small" />
|
|
|
|
|
+ <span v-else class="cm-detail-muted">暂无附件</span>
|
|
|
|
|
+ </span>
|
|
|
|
|
+ </div>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
@@ -415,7 +432,7 @@
|
|
|
<div class="cm-detail-flow-head">
|
|
<div class="cm-detail-flow-head">
|
|
|
<span class="cm-detail-flow-title">解除终止单 {{ item.terminationNo || '(编号未返回)' }}</span>
|
|
<span class="cm-detail-flow-title">解除终止单 {{ item.terminationNo || '(编号未返回)' }}</span>
|
|
|
<el-tag size="mini" :type="terminationStatusTagType(item.terminationStatus)">{{
|
|
<el-tag size="mini" :type="terminationStatusTagType(item.terminationStatus)">{{
|
|
|
- item.terminationStatus || '状态未返回' }}</el-tag>
|
|
|
|
|
|
|
+ item.terminationStatusText || '状态未返回' }}</el-tag>
|
|
|
<span class="cm-detail-flow-meta">业务类型:{{ item.typeText }}</span>
|
|
<span class="cm-detail-flow-meta">业务类型:{{ item.typeText }}</span>
|
|
|
<span class="cm-detail-flow-meta">生效日期:{{ item.effectiveDateText }}</span>
|
|
<span class="cm-detail-flow-meta">生效日期:{{ item.effectiveDateText }}</span>
|
|
|
<span class="cm-detail-flow-meta">最后工作日:{{ item.lastWorkingDateText }}</span>
|
|
<span class="cm-detail-flow-meta">最后工作日:{{ item.lastWorkingDateText }}</span>
|
|
@@ -559,6 +576,11 @@
|
|
|
<el-date-picker v-model="createForm.endDate" type="date" value-format="yyyy-MM-dd" placeholder="请选择终止日期"
|
|
<el-date-picker v-model="createForm.endDate" type="date" value-format="yyyy-MM-dd" placeholder="请选择终止日期"
|
|
|
style="width:100%;" />
|
|
style="width:100%;" />
|
|
|
</el-form-item>
|
|
</el-form-item>
|
|
|
|
|
+ <!-- 变更原因:仅变更模式展示;随变更草稿提交(changeReason),不再前端写死 -->
|
|
|
|
|
+ <el-form-item v-if="isChangeMode" label="变更原因" prop="changeReason">
|
|
|
|
|
+ <el-input v-model="createForm.changeReason" type="textarea" :rows="2" maxlength="200"
|
|
|
|
|
+ show-word-limit placeholder="请填写变更原因" />
|
|
|
|
|
+ </el-form-item>
|
|
|
<el-form-item label="签署方式" prop="signMethod">
|
|
<el-form-item label="签署方式" prop="signMethod">
|
|
|
<div class="cm-create-sign-method">
|
|
<div class="cm-create-sign-method">
|
|
|
<button type="button" class="cm-method-btn is-unavailable"
|
|
<button type="button" class="cm-method-btn is-unavailable"
|
|
@@ -894,17 +916,45 @@ const TERMINATE_REASON_FALLBACK = [
|
|
|
{ value: 'OTHER', label: '其他' },
|
|
{ value: 'OTHER', label: '其他' },
|
|
|
];
|
|
];
|
|
|
|
|
|
|
|
-/* 变更类型编码 → 文案:编码取自 swagger「劳动合同变更参数」changeType 说明
|
|
|
|
|
- (POSITION、SALARY、LOCATION、SCHEDULE、DEPARTMENT、OTHER),只做字面翻译 */
|
|
|
|
|
-const CHANGE_TYPE_LABELS = {
|
|
|
|
|
- POSITION: '岗位调整',
|
|
|
|
|
- SALARY: '薪资调整',
|
|
|
|
|
- LOCATION: '工作地点调整',
|
|
|
|
|
- SCHEDULE: '工作时间调整',
|
|
|
|
|
- DEPARTMENT: '部门调整',
|
|
|
|
|
- OTHER: '其他',
|
|
|
|
|
|
|
+/* 变更单 / 解除终止单的业务状态码空间一致(swagger 2026-10-19):
|
|
|
|
|
+ DRAFT、PENDING_APPROVAL、SIGNING、EFFECTIVE、REJECTED、CANCELLED。
|
|
|
|
|
+ 台账接口给了 changeStatusName / terminationStatusName 时用后端的,没给时用这张表翻中文,
|
|
|
|
|
+ 未知编码原样展示(不编造文案)。 */
|
|
|
|
|
+const FLOW_DOC_STATUS_LABELS = {
|
|
|
|
|
+ DRAFT: '草稿',
|
|
|
|
|
+ PENDING_APPROVAL: '审批中',
|
|
|
|
|
+ SIGNING: '签署中',
|
|
|
|
|
+ EFFECTIVE: '已生效',
|
|
|
|
|
+ REJECTED: '已驳回',
|
|
|
|
|
+ CANCELLED: '已取消',
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|
|
+/** 台账状态展示文案:后端中文名优先,缺失时按已知编码翻译,未知编码原样回显 */
|
|
|
|
|
+function flowDocStatusText(item = {}, codeKey, nameKey) {
|
|
|
|
|
+ const code = String(item[codeKey] || '').toUpperCase();
|
|
|
|
|
+ return (
|
|
|
|
|
+ item[nameKey] ||
|
|
|
|
|
+ FLOW_DOC_STATUS_LABELS[code] ||
|
|
|
|
|
+ item[codeKey] ||
|
|
|
|
|
+ ''
|
|
|
|
|
+ );
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/** 秒(10 位)/毫秒(13 位)时间戳 → 本地时区 YYYY-MM-DD;其余按日期字符串截取 */
|
|
|
|
|
+function detailDateText(value) {
|
|
|
|
|
+ if (!value) return '';
|
|
|
|
|
+ const str = String(value).trim();
|
|
|
|
|
+ if (/^\d{10}$/.test(str)) return tsToYMD(Number(str) * 1000);
|
|
|
|
|
+ if (/^\d{13}$/.test(str)) return tsToYMD(Number(str));
|
|
|
|
|
+ return str.slice(0, 10);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function tsToYMD(ms) {
|
|
|
|
|
+ const d = new Date(ms);
|
|
|
|
|
+ const pad = (n) => String(n).padStart(2, '0');
|
|
|
|
|
+ return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
/* 解除/终止业务类型:TERMINATE-解除、END-终止(swagger「劳动合同解除终止参数」) */
|
|
/* 解除/终止业务类型:TERMINATE-解除、END-终止(swagger「劳动合同解除终止参数」) */
|
|
|
const TERMINATION_TYPE_LABELS = { TERMINATE: '解除', END: '终止' };
|
|
const TERMINATION_TYPE_LABELS = { TERMINATE: '解除', END: '终止' };
|
|
|
|
|
|
|
@@ -1131,6 +1181,8 @@ function blankCreateForm() {
|
|
|
probationEnd: '',
|
|
probationEnd: '',
|
|
|
/* 试用期月数:由员工信息带出(probationPeriod,数字月数),只读;提交字段为 probationMonths */
|
|
/* 试用期月数:由员工信息带出(probationPeriod,数字月数),只读;提交字段为 probationMonths */
|
|
|
probationMonths: null,
|
|
probationMonths: null,
|
|
|
|
|
+ /* 变更原因:仅变更模式展示/提交(changeReason),不再前端写死 */
|
|
|
|
|
+ changeReason: '',
|
|
|
salary: '',
|
|
salary: '',
|
|
|
manager: '',
|
|
manager: '',
|
|
|
// 签署方式默认线下签订
|
|
// 签署方式默认线下签订
|
|
@@ -1207,6 +1259,10 @@ export default {
|
|
|
createVisible: false,
|
|
createVisible: false,
|
|
|
createMode: 'create',
|
|
createMode: 'create',
|
|
|
changeSnapshot: blankChangeSnapshot(),
|
|
changeSnapshot: blankChangeSnapshot(),
|
|
|
|
|
+ /* 编辑中的变更草稿主键:保存时带上 = 原地修改(2026-10-19 后端支持);空 = 新建 */
|
|
|
|
|
+ editingChangeId: '',
|
|
|
|
|
+ /* 编辑中的续签草稿主键(= 续签生成的新合同主键):保存时带上 = 原地修改;空 = 新建 */
|
|
|
|
|
+ editingRenewContractId: '',
|
|
|
createForm: blankCreateForm(),
|
|
createForm: blankCreateForm(),
|
|
|
createRules: {
|
|
createRules: {
|
|
|
employeeId: [{ required: true, message: '请选择员工', trigger: 'change' }],
|
|
employeeId: [{ required: true, message: '请选择员工', trigger: 'change' }],
|
|
@@ -1220,6 +1276,18 @@ export default {
|
|
|
positionType: [{ required: true, message: '请选择岗位性质', trigger: 'change' }],
|
|
positionType: [{ required: true, message: '请选择岗位性质', trigger: 'change' }],
|
|
|
startDate: [{ required: true, message: '请选择合同起始日期', trigger: 'change' }],
|
|
startDate: [{ required: true, message: '请选择合同起始日期', trigger: 'change' }],
|
|
|
endDate: [{ required: true, message: '请选择合同终止日期', trigger: 'change' }],
|
|
endDate: [{ required: true, message: '请选择合同终止日期', trigger: 'change' }],
|
|
|
|
|
+ changeReason: [
|
|
|
|
|
+ {
|
|
|
|
|
+ /* required: true 让必填星号显示;校验逻辑用 validator(仅变更模式必填) */
|
|
|
|
|
+ required: true,
|
|
|
|
|
+ validator: (_rule, value, callback) => {
|
|
|
|
|
+ if (!this.isChangeMode) return callback();
|
|
|
|
|
+ if (String(value || '').trim()) return callback();
|
|
|
|
|
+ callback(new Error('请填写变更原因'));
|
|
|
|
|
+ },
|
|
|
|
|
+ trigger: 'blur',
|
|
|
|
|
+ },
|
|
|
|
|
+ ],
|
|
|
signMethod: [{ required: true, message: '请选择签署方式', trigger: 'change' }],
|
|
signMethod: [{ required: true, message: '请选择签署方式', trigger: 'change' }],
|
|
|
templateId: [
|
|
templateId: [
|
|
|
{
|
|
{
|
|
@@ -1258,6 +1326,8 @@ export default {
|
|
|
detailCompareFields: DETAIL_COMPARE_FIELDS,
|
|
detailCompareFields: DETAIL_COMPARE_FIELDS,
|
|
|
terminateVisible: false,
|
|
terminateVisible: false,
|
|
|
terminateSubmitting: false,
|
|
terminateSubmitting: false,
|
|
|
|
|
+ /* 编辑中的解除草稿主键:保存时带上 = 原地修改(2026-10-19 后端支持);空 = 新建 */
|
|
|
|
|
+ terminateDraftId: '',
|
|
|
/* 解除草稿回填加载中:草稿态打开解除弹窗时先查详情 terminations */
|
|
/* 解除草稿回填加载中:草稿态打开解除弹窗时先查详情 terminations */
|
|
|
terminationLoading: false,
|
|
terminationLoading: false,
|
|
|
terminateForm: blankTerminateForm(),
|
|
terminateForm: blankTerminateForm(),
|
|
@@ -1606,12 +1676,23 @@ export default {
|
|
|
};
|
|
};
|
|
|
return map[tone] || 'info';
|
|
return map[tone] || 'info';
|
|
|
},
|
|
},
|
|
|
- /** 合同变更状态 tag 颜色:已知取值 DRAFT=变更草稿(抓包 2026-10-17),其余按编码关键字判定,识别不了用默认色 */
|
|
|
|
|
|
|
+ /** 合同变更状态 tag 颜色:与列表续签/解除状态同一套枚举配色(DRAFT=info、审批中=warning、
|
|
|
|
|
+ SIGNING=主色、EFFECTIVE=success、REJECTED=danger、CANCELLED=info);未知编码按关键字判定 */
|
|
|
changeStatusTagType(code) {
|
|
changeStatusTagType(code) {
|
|
|
const value = String(code || '').toUpperCase();
|
|
const value = String(code || '').toUpperCase();
|
|
|
|
|
+ const map = {
|
|
|
|
|
+ DRAFT: 'info',
|
|
|
|
|
+ PENDING_APPROVAL: 'warning',
|
|
|
|
|
+ SIGNING: '',
|
|
|
|
|
+ EFFECTIVE: 'success',
|
|
|
|
|
+ REJECTED: 'danger',
|
|
|
|
|
+ CANCELLED: 'info',
|
|
|
|
|
+ };
|
|
|
|
|
+ if (value in map) return map[value];
|
|
|
if (/APPROV|PENDING|SUBMIT|PROCESS/.test(value)) return 'warning';
|
|
if (/APPROV|PENDING|SUBMIT|PROCESS/.test(value)) return 'warning';
|
|
|
if (/EFFECT|COMPLETE|DONE|SUCCESS|PASS/.test(value)) return 'success';
|
|
if (/EFFECT|COMPLETE|DONE|SUCCESS|PASS/.test(value)) return 'success';
|
|
|
- if (/REJECT|CANCEL|VOID|FAIL/.test(value)) return 'info';
|
|
|
|
|
|
|
+ if (/REJECT|FAIL/.test(value)) return 'danger';
|
|
|
|
|
+ if (/CANCEL|VOID/.test(value)) return 'info';
|
|
|
if (/DRAFT/.test(value)) return 'info';
|
|
if (/DRAFT/.test(value)) return 'info';
|
|
|
return '';
|
|
return '';
|
|
|
},
|
|
},
|
|
@@ -2100,7 +2181,7 @@ export default {
|
|
|
statuses.includes(String(item.changeStatus || '').toUpperCase()),
|
|
statuses.includes(String(item.changeStatus || '').toUpperCase()),
|
|
|
);
|
|
);
|
|
|
}
|
|
}
|
|
|
- if (!picks.length) return;
|
|
|
|
|
|
|
+ if (!picks.length) return null;
|
|
|
/* 接口未承诺排序:多份变更单取 id 最大的(int64 以补零字符串比较) */
|
|
/* 接口未承诺排序:多份变更单取 id 最大的(int64 以补零字符串比较) */
|
|
|
const latest = picks.reduce((acc, cur) =>
|
|
const latest = picks.reduce((acc, cur) =>
|
|
|
String(cur.id ?? '').padStart(20, '0') > String(acc?.id ?? '').padStart(20, '0')
|
|
String(cur.id ?? '').padStart(20, '0') > String(acc?.id ?? '').padStart(20, '0')
|
|
@@ -2118,12 +2199,15 @@ export default {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
const form = this.createForm;
|
|
const form = this.createForm;
|
|
|
|
|
+ /* 变更原因不回填、不给默认值:一律留空由用户重新填写(旧草稿存的是当时写死的「合同变更」,不带回显) */
|
|
|
|
|
+ form.changeReason = '';
|
|
|
if (snapshot.contractType) form.contractType = snapshot.contractType;
|
|
if (snapshot.contractType) form.contractType = snapshot.contractType;
|
|
|
if (snapshot.salaryStandard != null && snapshot.salaryStandard !== '') {
|
|
if (snapshot.salaryStandard != null && snapshot.salaryStandard !== '') {
|
|
|
form.salary = String(snapshot.salaryStandard);
|
|
form.salary = String(snapshot.salaryStandard);
|
|
|
}
|
|
}
|
|
|
- if (snapshot.startDate) form.startDate = String(snapshot.startDate).slice(0, 10);
|
|
|
|
|
- if (snapshot.endDate) form.endDate = String(snapshot.endDate).slice(0, 10);
|
|
|
|
|
|
|
+ /* 快照日期可能是秒/毫秒时间戳(后端 beforeSnapshot 实测如此),统一归一成 YYYY-MM-DD */
|
|
|
|
|
+ if (snapshot.startDate) form.startDate = detailDateText(snapshot.startDate);
|
|
|
|
|
+ if (snapshot.endDate) form.endDate = detailDateText(snapshot.endDate);
|
|
|
if (snapshot.probationMonths != null && snapshot.probationMonths !== '') {
|
|
if (snapshot.probationMonths != null && snapshot.probationMonths !== '') {
|
|
|
form.probationMonths = Number(snapshot.probationMonths);
|
|
form.probationMonths = Number(snapshot.probationMonths);
|
|
|
}
|
|
}
|
|
@@ -2147,6 +2231,8 @@ export default {
|
|
|
form.positionType = String(snapshot.positionType);
|
|
form.positionType = String(snapshot.positionType);
|
|
|
}
|
|
}
|
|
|
if (snapshot.positionTypeName) form.positionTypeName = snapshot.positionTypeName;
|
|
if (snapshot.positionTypeName) form.positionTypeName = snapshot.positionTypeName;
|
|
|
|
|
+ /* 返回草稿(或最近变更单)主键:调用方保存时带上它 = 原地修改这份草稿 */
|
|
|
|
|
+ return latest.id || null;
|
|
|
},
|
|
},
|
|
|
/**
|
|
/**
|
|
|
* 打开变更弹框后按已回填的 部门 / 序列 / 岗位 / 性质 逐级加载下拉选项,
|
|
* 打开变更弹框后按已回填的 部门 / 序列 / 岗位 / 性质 逐级加载下拉选项,
|
|
@@ -2421,6 +2507,11 @@ export default {
|
|
|
buildDetailChange(item = {}) {
|
|
buildDetailChange(item = {}) {
|
|
|
const before = this.snapshotToCompareModel(this.parseSnapshot(item.beforeSnapshot));
|
|
const before = this.snapshotToCompareModel(this.parseSnapshot(item.beforeSnapshot));
|
|
|
const after = this.snapshotToCompareModel(this.parseSnapshot(item.afterSnapshot));
|
|
const after = this.snapshotToCompareModel(this.parseSnapshot(item.afterSnapshot));
|
|
|
|
|
+ /* 「变更后签署方式」的权威值是变更单顶层的 signingMethod(提交时即为变更后的值);
|
|
|
|
|
+ 历史草稿的 afterSnapshot 可能没写签署方式(早期提交未入快照),缺失时取顶层,不猜 */
|
|
|
|
|
+ if (after && !after.signingMethod && item.signingMethod) {
|
|
|
|
|
+ after.signingMethod = signMethodCode(item.signingMethod);
|
|
|
|
|
+ }
|
|
|
let gap = '';
|
|
let gap = '';
|
|
|
if (!before && !after) {
|
|
if (!before && !after) {
|
|
|
gap = '该变更单未返回变更前 / 变更后快照(beforeSnapshot / afterSnapshot),无法展示变更内容,需后端补充。';
|
|
gap = '该变更单未返回变更前 / 变更后快照(beforeSnapshot / afterSnapshot),无法展示变更内容,需后端补充。';
|
|
@@ -2431,8 +2522,7 @@ export default {
|
|
|
id: item.id,
|
|
id: item.id,
|
|
|
changeNo: item.changeNo || '',
|
|
changeNo: item.changeNo || '',
|
|
|
changeStatus: item.changeStatus || '',
|
|
changeStatus: item.changeStatus || '',
|
|
|
- changeTypeText:
|
|
|
|
|
- CHANGE_TYPE_LABELS[String(item.changeType || '').toUpperCase()] || item.changeType || '-',
|
|
|
|
|
|
|
+ changeStatusText: flowDocStatusText(item, 'changeStatus', 'changeStatusName'),
|
|
|
effectiveDateText: this.formatDetailDate(item.effectiveDate),
|
|
effectiveDateText: this.formatDetailDate(item.effectiveDate),
|
|
|
signingMethodText: signMethodLabel(item.signingMethod) || '-',
|
|
signingMethodText: signMethodLabel(item.signingMethod) || '-',
|
|
|
documentCode: item.documentCode || '',
|
|
documentCode: item.documentCode || '',
|
|
@@ -2478,6 +2568,7 @@ export default {
|
|
|
id: item.id,
|
|
id: item.id,
|
|
|
terminationNo: item.terminationNo || '',
|
|
terminationNo: item.terminationNo || '',
|
|
|
terminationStatus: item.terminationStatus || '',
|
|
terminationStatus: item.terminationStatus || '',
|
|
|
|
|
+ terminationStatusText: flowDocStatusText(item, 'terminationStatus', 'terminationStatusName'),
|
|
|
typeText:
|
|
typeText:
|
|
|
TERMINATION_TYPE_LABELS[String(item.terminationType || '').toUpperCase()] ||
|
|
TERMINATION_TYPE_LABELS[String(item.terminationType || '').toUpperCase()] ||
|
|
|
item.terminationType ||
|
|
item.terminationType ||
|
|
@@ -2557,10 +2648,10 @@ export default {
|
|
|
case 'signMethod':
|
|
case 'signMethod':
|
|
|
return signMethodLabel(raw) || '-';
|
|
return signMethodLabel(raw) || '-';
|
|
|
case 'date':
|
|
case 'date':
|
|
|
- return raw ? String(raw).slice(0, 10) : '-';
|
|
|
|
|
|
|
+ return detailDateText(raw) || '-';
|
|
|
case 'dateEnd':
|
|
case 'dateEnd':
|
|
|
/* 终止日期为空 = 无固定期限 */
|
|
/* 终止日期为空 = 无固定期限 */
|
|
|
- return raw ? String(raw).slice(0, 10) : '长期';
|
|
|
|
|
|
|
+ return detailDateText(raw) || '长期';
|
|
|
default:
|
|
default:
|
|
|
return raw === '' || raw === null || raw === undefined ? '-' : String(raw);
|
|
return raw === '' || raw === null || raw === undefined ? '-' : String(raw);
|
|
|
}
|
|
}
|
|
@@ -2584,14 +2675,13 @@ export default {
|
|
|
}
|
|
}
|
|
|
if (field.type === 'signMethod') return signMethodCode(value);
|
|
if (field.type === 'signMethod') return signMethodCode(value);
|
|
|
if (field.type === 'date' || field.type === 'dateEnd') {
|
|
if (field.type === 'date' || field.type === 'dateEnd') {
|
|
|
- return value ? String(value).slice(0, 10) : '';
|
|
|
|
|
|
|
+ return detailDateText(value);
|
|
|
}
|
|
}
|
|
|
return String(value).trim();
|
|
return String(value).trim();
|
|
|
},
|
|
},
|
|
|
- /** 快照里的日期可能带时间(2026-10-17T00:00:00)→ 只留日期;空值显示 — */
|
|
|
|
|
|
|
+ /** 生效日期 / 最后工作日等台账日期:兼容日期串与秒/毫秒时间戳;空值显示 — */
|
|
|
formatDetailDate(value) {
|
|
formatDetailDate(value) {
|
|
|
- if (!value) return '-';
|
|
|
|
|
- return String(value).slice(0, 10);
|
|
|
|
|
|
|
+ return detailDateText(value) || '-';
|
|
|
},
|
|
},
|
|
|
/**
|
|
/**
|
|
|
* 导出劳动合同台账。
|
|
* 导出劳动合同台账。
|
|
@@ -2793,6 +2883,8 @@ export default {
|
|
|
this.createVisible = false;
|
|
this.createVisible = false;
|
|
|
this.createMode = 'create';
|
|
this.createMode = 'create';
|
|
|
this.changeSnapshot = blankChangeSnapshot();
|
|
this.changeSnapshot = blankChangeSnapshot();
|
|
|
|
|
+ this.editingChangeId = '';
|
|
|
|
|
+ this.editingRenewContractId = '';
|
|
|
this.createForm = blankCreateForm();
|
|
this.createForm = blankCreateForm();
|
|
|
this.clearCreateQuery();
|
|
this.clearCreateQuery();
|
|
|
if (typeof done === 'function') done();
|
|
if (typeof done === 'function') done();
|
|
@@ -2891,8 +2983,13 @@ export default {
|
|
|
随后用该主键 + hr_contract_change_approval 发起审批,终态通过后才把字段应用到合同上。
|
|
随后用该主键 + hr_contract_change_approval 发起审批,终态通过后才把字段应用到合同上。
|
|
|
所以这里必须把返回的变更单主键带出去,不能用合同 id 顶替。 */
|
|
所以这里必须把返回的变更单主键带出去,不能用合同 id 顶替。 */
|
|
|
const changeId = await changeContract(form.id, compact({
|
|
const changeId = await changeContract(form.id, compact({
|
|
|
|
|
+ /* ★ 2026-10-19 后端支持:携带变更草稿主键 = 原地修改该草稿(不传 = 新建);
|
|
|
|
|
+ 编辑已有草稿时必须带上,否则会撞「已有草稿」校验 */
|
|
|
|
|
+ id: this.editingChangeId || undefined,
|
|
|
|
|
+ /* ★ 变更类型仍是接口枚举必填项,前端目前只有「其他」一种业务口径,固定 OTHER;
|
|
|
|
|
+ 变更原因改为用户在表单里填写(changeReason),不再写死「合同变更」 */
|
|
|
changeType: 'OTHER',
|
|
changeType: 'OTHER',
|
|
|
- changeReason: '合同变更',
|
|
|
|
|
|
|
+ changeReason: String(form.changeReason).trim(),
|
|
|
effectiveDate: form.startDate || todayStr(),
|
|
effectiveDate: form.startDate || todayStr(),
|
|
|
signingMethod: payload.signingMethod,
|
|
signingMethod: payload.signingMethod,
|
|
|
offlineFileId: form.signMethod === '线下' ? (form.fileIds?.[0] || form.fileId) : undefined,
|
|
offlineFileId: form.signMethod === '线下' ? (form.fileIds?.[0] || form.fileId) : undefined,
|
|
@@ -2916,6 +3013,9 @@ export default {
|
|
|
positionLevel: payload.positionLevel,
|
|
positionLevel: payload.positionLevel,
|
|
|
positionType: payload.positionType,
|
|
positionType: payload.positionType,
|
|
|
positionTypeName: payload.positionTypeName,
|
|
positionTypeName: payload.positionTypeName,
|
|
|
|
|
+ /* 签署方式与合同附件也是变更内容:不进快照,详情「变更后」就看不到(2026-10-19 用户反馈) */
|
|
|
|
|
+ signingMethod: payload.signingMethod,
|
|
|
|
|
+ attachmentIds: payload.attachmentIds,
|
|
|
}),
|
|
}),
|
|
|
}));
|
|
}));
|
|
|
return { id: form.id, changeId, payload };
|
|
return { id: form.id, changeId, payload };
|
|
@@ -2930,6 +3030,8 @@ export default {
|
|
|
...payload,
|
|
...payload,
|
|
|
renewal: true,
|
|
renewal: true,
|
|
|
probationMonths: 0,
|
|
probationMonths: 0,
|
|
|
|
|
+ /* ★ 2026-10-19 后端支持:携带续签草稿(新合同)主键 = 原地修改该草稿 */
|
|
|
|
|
+ id: this.editingRenewContractId || undefined,
|
|
|
}),
|
|
}),
|
|
|
});
|
|
});
|
|
|
/* 拿不到新合同主键时**不能**退回原合同 id —— 那会把审批发到旧合同上 */
|
|
/* 拿不到新合同主键时**不能**退回原合同 id —— 那会把审批发到旧合同上 */
|
|
@@ -3312,7 +3414,9 @@ export default {
|
|
|
this.openEdit(row);
|
|
this.openEdit(row);
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
- this.openRenew(row);
|
|
|
|
|
|
|
+ /* 续签草稿(renewalStatusCode === 'DRAFT'):草稿单据就是续签生成的新合同,
|
|
|
|
|
+ 载入其值原地修改(renewalContractId,2026-10-19 契约确认) */
|
|
|
|
|
+ this.openRenew(row, { draftContractId: row.renewalContractId || '' });
|
|
|
},
|
|
},
|
|
|
/**
|
|
/**
|
|
|
* 提交续签审批:审批对象是**续签生成的新合同**(businessKey 与签订相同 hr_contract_approval)。
|
|
* 提交续签审批:审批对象是**续签生成的新合同**(businessKey 与签订相同 hr_contract_approval)。
|
|
@@ -3541,12 +3645,14 @@ export default {
|
|
|
this.createMode = 'change';
|
|
this.createMode = 'change';
|
|
|
this.createForm = blankCreateForm();
|
|
this.createForm = blankCreateForm();
|
|
|
this.changeSnapshot = blankChangeSnapshot();
|
|
this.changeSnapshot = blankChangeSnapshot();
|
|
|
|
|
+ this.editingChangeId = '';
|
|
|
this.createVisible = true;
|
|
this.createVisible = true;
|
|
|
this.loading = true;
|
|
this.loading = true;
|
|
|
try {
|
|
try {
|
|
|
await this.fillCreateFormFromContract(row);
|
|
await this.fillCreateFormFromContract(row);
|
|
|
- /* 变更草稿回显:该合同挂着一份 DRAFT 变更单时,把变更后的值回填表单再编辑 */
|
|
|
|
|
- await this.restoreChangeSnapshot(row.id, ['DRAFT']);
|
|
|
|
|
|
|
+ /* 变更草稿回显:该合同挂着一份 DRAFT 变更单时,把变更后的值回填表单再编辑;
|
|
|
|
|
+ 返回草稿主键,保存时带上 = 原地修改这份草稿(2026-10-19 后端支持) */
|
|
|
|
|
+ this.editingChangeId = (await this.restoreChangeSnapshot(row.id, ['DRAFT'])) || '';
|
|
|
/* 变更模式的机构/部门/岗位级联下拉:按已回填的值逐级加载选项,避免回显成裸 id */
|
|
/* 变更模式的机构/部门/岗位级联下拉:按已回填的值逐级加载选项,避免回显成裸 id */
|
|
|
await this.loadChangeCascadeOptions();
|
|
await this.loadChangeCascadeOptions();
|
|
|
this.$nextTick(() => this.$refs.createFormRef?.clearValidate());
|
|
this.$nextTick(() => this.$refs.createFormRef?.clearValidate());
|
|
@@ -3557,18 +3663,20 @@ export default {
|
|
|
this.loading = false;
|
|
this.loading = false;
|
|
|
}
|
|
}
|
|
|
},
|
|
},
|
|
|
- async openRenew(row) {
|
|
|
|
|
|
|
+ async openRenew(row, { draftContractId = '' } = {}) {
|
|
|
if (!row?.id) {
|
|
if (!row?.id) {
|
|
|
this.$message.warning('缺少合同主键,无法续签');
|
|
this.$message.warning('缺少合同主键,无法续签');
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
- if (!isEffective(row)) {
|
|
|
|
|
|
|
+ if (!isEffective(row) && !draftContractId) {
|
|
|
this.$message.warning('仅审批通过并签署成功的合同可以续签');
|
|
this.$message.warning('仅审批通过并签署成功的合同可以续签');
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
this.createMode = 'renew';
|
|
this.createMode = 'renew';
|
|
|
this.createForm = blankCreateForm();
|
|
this.createForm = blankCreateForm();
|
|
|
this.changeSnapshot = blankChangeSnapshot();
|
|
this.changeSnapshot = blankChangeSnapshot();
|
|
|
|
|
+ /* 续签草稿主键(= 续签生成的新合同主键):保存时带上 = 原地修改该草稿 */
|
|
|
|
|
+ this.editingRenewContractId = draftContractId || '';
|
|
|
this.createVisible = true;
|
|
this.createVisible = true;
|
|
|
this.loading = true;
|
|
this.loading = true;
|
|
|
try {
|
|
try {
|
|
@@ -3581,6 +3689,12 @@ export default {
|
|
|
this.createForm.endDate = addMonths(this.createForm.startDate, months);
|
|
this.createForm.endDate = addMonths(this.createForm.startDate, months);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
+ /* 续签草稿回填:草稿单据就是续签生成的新合同,载入它的值覆盖默认值
|
|
|
|
|
+ (changeSnapshot 仍保留原合同,差异高亮不受影响) */
|
|
|
|
|
+ if (draftContractId) {
|
|
|
|
|
+ await this.fillCreateFormFromContract({ id: draftContractId });
|
|
|
|
|
+ this.createForm.contractNo = '';
|
|
|
|
|
+ }
|
|
|
this.$nextTick(() => this.$refs.createFormRef?.clearValidate());
|
|
this.$nextTick(() => this.$refs.createFormRef?.clearValidate());
|
|
|
} catch (e) {
|
|
} catch (e) {
|
|
|
this.$message.error(e.message || '合同详情加载失败');
|
|
this.$message.error(e.message || '合同详情加载失败');
|
|
@@ -3601,6 +3715,8 @@ export default {
|
|
|
this.$message.warning('仅审批通过并签署成功的合同可以解除');
|
|
this.$message.warning('仅审批通过并签署成功的合同可以解除');
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
+ /* 编辑中的解除草稿主键:保存时带上 = 原地修改该草稿(2026-10-19 后端支持) */
|
|
|
|
|
+ this.terminateDraftId = draftMode ? row.terminationId || '' : '';
|
|
|
this.terminateForm = {
|
|
this.terminateForm = {
|
|
|
...blankTerminateForm(),
|
|
...blankTerminateForm(),
|
|
|
id: row.id,
|
|
id: row.id,
|
|
@@ -3612,16 +3728,18 @@ export default {
|
|
|
this.terminateVisible = true;
|
|
this.terminateVisible = true;
|
|
|
this.$nextTick(() => this.$refs.terminateFormRef?.clearValidate());
|
|
this.$nextTick(() => this.$refs.terminateFormRef?.clearValidate());
|
|
|
if (!draftMode) return;
|
|
if (!draftMode) return;
|
|
|
- /* 草稿态:查详情台账回填解除草稿;查不到如实提示,不拿空白表单冒充 */
|
|
|
|
|
|
|
+ /* 草稿态:优先用列表行返回的解除单主键(terminationId,2026-10-19 契约确认),
|
|
|
|
|
+ 在详情台账里精确定位草稿;没有主键再按 DRAFT 状态找 */
|
|
|
this.terminationLoading = true;
|
|
this.terminationLoading = true;
|
|
|
try {
|
|
try {
|
|
|
- const draft = await this.resolveTerminationDraft(row.id);
|
|
|
|
|
|
|
+ const draft = await this.resolveTerminationDraft(row.id, row.terminationId);
|
|
|
if (!draft) {
|
|
if (!draft) {
|
|
|
this.$message.warning(
|
|
this.$message.warning(
|
|
|
'该合同存在解除草稿,但详情接口未返回草稿明细(terminations),无法回填,需后端补充',
|
|
'该合同存在解除草稿,但详情接口未返回草稿明细(terminations),无法回填,需后端补充',
|
|
|
);
|
|
);
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
|
|
+ this.terminateDraftId = draft.id || row.terminationId || '';
|
|
|
this.terminateForm = {
|
|
this.terminateForm = {
|
|
|
...this.terminateForm,
|
|
...this.terminateForm,
|
|
|
terminationType: draft.terminationType || this.terminateForm.terminationType,
|
|
terminationType: draft.terminationType || this.terminateForm.terminationType,
|
|
@@ -3639,14 +3757,20 @@ export default {
|
|
|
}
|
|
}
|
|
|
},
|
|
},
|
|
|
/**
|
|
/**
|
|
|
- * 查该合同的解除草稿单据:详情接口 terminations 台账里
|
|
|
|
|
- * terminationStatus === 'DRAFT' 的那一条(含解除终止主键 id)。
|
|
|
|
|
- * 查不到返回 null —— 由调用方明确提示,不编造。
|
|
|
|
|
|
|
+ * 查该合同的解除草稿单据:详情接口 terminations 台账里找草稿。
|
|
|
|
|
+ * ★ 优先用 terminationId(列表行 / 2026-10-19 契约确认的字段)精确匹配;
|
|
|
|
|
+ * 没传主键时按 terminationStatus === 'DRAFT' 找。查不到返回 null —— 不编造。
|
|
|
*/
|
|
*/
|
|
|
- async resolveTerminationDraft(contractId) {
|
|
|
|
|
|
|
+ async resolveTerminationDraft(contractId, terminationId = '') {
|
|
|
if (!contractId) return null;
|
|
if (!contractId) return null;
|
|
|
const detail = await getContractById(contractId);
|
|
const detail = await getContractById(contractId);
|
|
|
const terminations = Array.isArray(detail?.terminations) ? detail.terminations : [];
|
|
const terminations = Array.isArray(detail?.terminations) ? detail.terminations : [];
|
|
|
|
|
+ if (terminationId) {
|
|
|
|
|
+ const hit = terminations.find(
|
|
|
|
|
+ (item) => String(item?.id ?? '') === String(terminationId),
|
|
|
|
|
+ );
|
|
|
|
|
+ if (hit) return hit;
|
|
|
|
|
+ }
|
|
|
return (
|
|
return (
|
|
|
terminations.find(
|
|
terminations.find(
|
|
|
(item) => String(item?.terminationStatus || '').toUpperCase() === 'DRAFT',
|
|
(item) => String(item?.terminationStatus || '').toUpperCase() === 'DRAFT',
|
|
@@ -3671,6 +3795,8 @@ export default {
|
|
|
this.terminateSubmitting = true;
|
|
this.terminateSubmitting = true;
|
|
|
try {
|
|
try {
|
|
|
const result = await terminateContract(form.id, compact({
|
|
const result = await terminateContract(form.id, compact({
|
|
|
|
|
+ /* ★ 2026-10-19 后端支持:携带解除草稿主键 = 原地修改该草稿(不传 = 新建) */
|
|
|
|
|
+ id: this.terminateDraftId || undefined,
|
|
|
terminationType: form.terminationType,
|
|
terminationType: form.terminationType,
|
|
|
reason: form.reason,
|
|
reason: form.reason,
|
|
|
reasonCode: form.reasonCode,
|
|
reasonCode: form.reasonCode,
|
|
@@ -3679,7 +3805,10 @@ export default {
|
|
|
signingMethod,
|
|
signingMethod,
|
|
|
templateId: signingMethod === 'ONLINE' ? form.templateId : undefined,
|
|
templateId: signingMethod === 'ONLINE' ? form.templateId : undefined,
|
|
|
templateVersionId: signingMethod === 'ONLINE' ? form.templateVersionId : undefined,
|
|
templateVersionId: signingMethod === 'ONLINE' ? form.templateVersionId : undefined,
|
|
|
- offlineFileId: signingMethod === 'OFFLINE' ? form.offlineFileIds?.[0] : undefined,
|
|
|
|
|
|
|
+ /* ★ 契约字段是 attachmentIds(英文逗号分隔字符串,可多个);
|
|
|
|
|
+ 之前误传 offlineFileId 单值键,后端收不到 → 解除详情一直「暂无附件」(2026-10-19 用户反馈) */
|
|
|
|
|
+ attachmentIds:
|
|
|
|
|
+ signingMethod === 'OFFLINE' ? joinAttachmentIds(form.offlineFileIds) : undefined,
|
|
|
}));
|
|
}));
|
|
|
/* 接口返回解除/终止草稿主键(long),与变更一样:审批对象就是这张草稿单据 */
|
|
/* 接口返回解除/终止草稿主键(long),与变更一样:审批对象就是这张草稿单据 */
|
|
|
if (!result) {
|
|
if (!result) {
|