-
-
确认销假
+
+
+
+ {{ wf.type }}
+
+ {{ wf.leaveType }}
+ {{ wf.billNo }}
+ {{ wf.time }}
+ 查看申请
+
+ 当前为{{ detail.statusText }}状态,暂无关联流程记录
+
@@ -219,6 +213,12 @@ export default {
default:
return 'danger'
}
+ },
+ hasRelatedWorkflows() {
+ return this.detail && Array.isArray(this.detail.relatedWorkflows) && this.detail.relatedWorkflows.length > 0
+ },
+ isLeaveOrSickStatus() {
+ return this.detail && (this.detail.status === 4 || this.detail.status === 5)
}
},
methods: {
@@ -265,33 +265,33 @@ export default {
await this.loadDetail()
this.$emit('refresh')
},
- goToLeaveApply() {
- if (!this.detail || !this.detail.leaveApply || !this.detail.leaveApply.id) return
- const leaveType = this.detail.leaveApply.leaveType || ''
- const isPersonal = leaveType === '事假' || leaveType === '病假'
+ workflowTagType(type) {
+ if (type === '请假') return 'danger'
+ if (type === '补卡') return 'warning'
+ return 'info'
+ },
+ goToLeaveApply(wf) {
+ if (!wf || !wf.id) return
+ const lt = wf.leaveType || ''
+ const isPersonal = lt === '事假' || lt === '病假'
const path = isPersonal ? '/workFlow/personal-leave-apply' : '/workFlow/paid-leave-apply'
- this.$router.push({
- path,
- query: { id: this.detail.leaveApply.id }
- })
+ this.$router.push({ path, query: { id: wf.id } })
},
async cancelLeave() {
if (!this.detail || !this.detail.id) return
- if (!(this.detail.status === 4 || this.detail.status === 5)) {
+ if (!this.isLeaveOrSickStatus) {
this.$message.warning('当前考勤记录不是请假/病假状态,无法销假')
return
}
this.cancelLoading = true
try {
- await this.$confirm('确定要对该考勤记录销假吗?', '提示', { type: 'warning' })
- await cancelAttendanceLeave({
- id: this.detail.id
- })
+ await this.$confirm('确定要对该考勤记录销假吗?销假后将根据打卡记录重新判定考勤状态。', '确认销假', { type: 'warning' })
+ await cancelAttendanceLeave({ id: this.detail.id })
this.$message.success('销假成功')
await this.loadDetail()
this.$emit('refresh')
} catch (e) {
- // eslint-disable-next-line no-empty
+ // user cancelled or api error
} finally {
this.cancelLoading = false
}
@@ -496,6 +496,53 @@ export default {
grid-column: 1 / -1;
}
+.workflow-card {
+ margin-top: 16px;
+}
+
+.workflow-list {
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+}
+
+.workflow-item {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ padding: 8px 12px;
+ border-radius: 8px;
+ background: #f8f9fb;
+ font-size: 13px;
+}
+
+.workflow-item__tag {
+ flex-shrink: 0;
+}
+
+.workflow-item__leave-type {
+ color: #606266;
+ font-weight: 500;
+}
+
+.workflow-item__bill {
+ color: #409eff;
+ font-family: monospace;
+}
+
+.workflow-item__time {
+ color: #909399;
+ font-size: 12px;
+ margin-left: auto;
+}
+
+.workflow-empty {
+ color: #909399;
+ font-size: 13px;
+ text-align: center;
+ padding: 12px 0;
+}
+
@media (max-width: 960px) {
.overview-grid,
diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqAttendanceRecord/RelatedWorkflowItem.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqAttendanceRecord/RelatedWorkflowItem.cs
new file mode 100644
index 0000000..836e615
--- /dev/null
+++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Dto/LqAttendanceRecord/RelatedWorkflowItem.cs
@@ -0,0 +1,38 @@
+namespace NCC.Extend.Entitys.Dto.LqAttendanceRecord
+{
+ ///
+ /// 考勤记录关联流程项(存储在 F_RelatedWorkflowsJson 字段中的数组元素)
+ ///
+ public class RelatedWorkflowItem
+ {
+ ///
+ /// 流程类型:请假 | 补卡
+ ///
+ public string type { get; set; }
+
+ ///
+ /// 流程/表单主键(WFORM_LEAVEAPPLY.F_Id 等)
+ ///
+ public string id { get; set; }
+
+ ///
+ /// 单号(如 YGQJ-202604020008)
+ ///
+ public string billNo { get; set; }
+
+ ///
+ /// 假种(仅请假类型有值,如"年假"/"事假"/"病假")
+ ///
+ public string leaveType { get; set; }
+
+ ///
+ /// 备注
+ ///
+ public string remark { get; set; }
+
+ ///
+ /// 写入时间
+ ///
+ public string time { get; set; }
+ }
+}
diff --git a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_attendance_record/LqAttendanceRecordEntity.cs b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_attendance_record/LqAttendanceRecordEntity.cs
index b70135b..286b984 100644
--- a/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_attendance_record/LqAttendanceRecordEntity.cs
+++ b/netcore/src/Modularity/Extend/NCC.Extend.Entitys/Entity/lq_attendance_record/LqAttendanceRecordEntity.cs
@@ -240,6 +240,12 @@ namespace NCC.Extend.Entitys.lq_attendance_record
public DateTime? UpdateTime { get; set; }
///
+ /// 关联流程JSON(所有与该考勤记录关联的流程信息,含请假/补卡等)
+ ///
+ [SugarColumn(ColumnName = "F_RelatedWorkflowsJson", ColumnDataType = "longtext")]
+ public string RelatedWorkflowsJson { get; set; }
+
+ ///
/// 是否有效(1-有效,0-无效)
///
[SugarColumn(ColumnName = "F_IsEffective")]
diff --git a/netcore/src/Modularity/Extend/NCC.Extend/LqAttendanceRecordService.cs b/netcore/src/Modularity/Extend/NCC.Extend/LqAttendanceRecordService.cs
index 115f912..6afb2fd 100644
--- a/netcore/src/Modularity/Extend/NCC.Extend/LqAttendanceRecordService.cs
+++ b/netcore/src/Modularity/Extend/NCC.Extend/LqAttendanceRecordService.cs
@@ -25,6 +25,7 @@ using NCC.Extend.Entitys.lq_attendance_missing_card_rule;
using NCC.Extend.Entitys.lq_attendance_record;
using NCC.Extend.Entitys.lq_attendance_setting;
using NCC.Extend.Entitys.lq_mdxx;
+using NCC.Extend.Entitys.wform_leaveapply;
using NCC.Extend.Interfaces.LqAttendanceRecord;
using NCC.FriendlyException;
using Newtonsoft.Json;
@@ -361,24 +362,41 @@ namespace NCC.Extend
record = recalculated.FirstOrDefault() ?? record;
var group = await GetAttendanceGroupAsync(record.AttendanceGroupId, false);
- // 如果当前状态来自“请假/病假同步写入”,则尝试从 Remark 中解析单号并反查请假单信息,便于在考勤详情中跳转流程/操作销假
- string leaveApplyId = null;
- string leaveApplyBillNo = null;
- string leaveApplyType = null;
+ var relatedWorkflows = ParseRelatedWorkflows(record.RelatedWorkflowsJson);
- if (TryParseLeaveApplyFromSyncRemark(record.Remark, out var parsedLeaveType, out var parsedBillNo)
+ var isLeaveStatus = record.Status == (int)AttendanceRecordStatusEnum.请假
+ || record.Status == (int)AttendanceRecordStatusEnum.病假;
+ if (!relatedWorkflows.Any(x => x.type == "请假") && isLeaveStatus
+ && TryParseLeaveApplyFromSyncRemark(record.Remark, out var parsedLeaveType, out var parsedBillNo)
&& !string.IsNullOrWhiteSpace(parsedBillNo))
{
- leaveApplyBillNo = parsedBillNo;
- leaveApplyType = parsedLeaveType;
- var leaveApply = await _db.Queryable
()
+ var leaveApply = await _db.Queryable()
.Where(x => x.BillNo == parsedBillNo)
.WhereIF(!string.IsNullOrWhiteSpace(parsedLeaveType), x => x.LeaveType == parsedLeaveType)
.FirstAsync();
- leaveApplyId = leaveApply?.Id;
+
+ relatedWorkflows.Add(new RelatedWorkflowItem
+ {
+ type = "请假",
+ id = leaveApply?.Id,
+ billNo = parsedBillNo,
+ leaveType = parsedLeaveType,
+ remark = record.Remark
+ });
+ }
+
+ if (!string.IsNullOrWhiteSpace(record.SupplementWorkflowId) && !relatedWorkflows.Any(x => x.type == "补卡"))
+ {
+ relatedWorkflows.Add(new RelatedWorkflowItem
+ {
+ type = "补卡",
+ id = record.SupplementWorkflowId,
+ remark = record.SupplementRemark,
+ time = record.SupplementTime?.ToString("yyyy-MM-dd HH:mm:ss")
+ });
}
- return BuildDetailResult(record, group, leaveApplyId, leaveApplyBillNo, leaveApplyType);
+ return BuildDetailResult(record, group, relatedWorkflows);
}
///
@@ -411,13 +429,8 @@ namespace NCC.Extend
throw NCCException.Oh("当前考勤记录不是请假/病假状态,无法销假");
}
- // 通过 Remark 解析请假单信息(用于展示与记录销假备注)
- if (!TryParseLeaveApplyFromSyncRemark(record.Remark, out var leaveType, out var billNo))
- {
- throw NCCException.Oh("请假备注格式不正确,无法解析销假所需信息");
- }
+ TryParseLeaveApplyFromSyncRemark(record.Remark, out var leaveType, out var billNo);
- // 撤销对该日期的请假影响:根据假期/免考勤/打卡情况,把该日期恢复为“休息/正常/缺卡”等对应考勤状态
var attendanceDate = record.AttendanceDate.Date;
var isHoliday = await IsHolidayAsync(attendanceDate);
var isExempt = await IsExemptAsync(record.UserId, attendanceDate);
@@ -445,13 +458,12 @@ namespace NCC.Extend
record.LateMinutes = 0;
record.EarlyLeaveMinutes = 0;
record.IsManual = 1;
- var cancelText = string.IsNullOrWhiteSpace(input.OperatorOpinion)
- ? "已销假"
- : input.OperatorOpinion.Trim();
- // 保留原 Remark 前缀,便于在考勤详情中仍能反查到对应请假单信息
- record.Remark = $"请假审批通过:{leaveType}(单号 {billNo});销假:{cancelText}";
-
- // 置空快照,确保 RecalculateRecordStatusesAsync 会重新生成规则快照
+ var cancelText = string.IsNullOrWhiteSpace(input.OperatorOpinion) ? "已销假" : input.OperatorOpinion.Trim();
+ var hasLeaveInfo = !string.IsNullOrWhiteSpace(leaveType) && !string.IsNullOrWhiteSpace(billNo);
+ record.Remark = hasLeaveInfo
+ ? $"请假审批通过:{leaveType}(单号 {billNo});销假:{cancelText}"
+ : $"销假:{cancelText}";
+ record.RuleSnapshotJson = null;
record.RuleSnapshotJson = null;
record.AllRuleSnapshotJson = null;
@@ -616,6 +628,17 @@ namespace NCC.Extend
record.UpdateUserId = operatorUserId;
record.UpdateTime = record.SupplementTime;
+ if (!string.IsNullOrWhiteSpace(input.WorkflowId))
+ {
+ record.RelatedWorkflowsJson = AppendRelatedWorkflow(record.RelatedWorkflowsJson, new RelatedWorkflowItem
+ {
+ type = "补卡",
+ id = input.WorkflowId.Trim(),
+ remark = input.Remark?.Trim(),
+ time = record.SupplementTime?.ToString("yyyy-MM-dd HH:mm:ss")
+ });
+ }
+
await _db.Updateable(record).ExecuteCommandAsync();
return new
@@ -1418,10 +1441,9 @@ namespace NCC.Extend
private static dynamic BuildDetailResult(
LqAttendanceRecordEntity record,
LqAttendanceGroupEntity group,
- string leaveApplyId = null,
- string leaveApplyBillNo = null,
- string leaveApplyType = null)
+ List relatedWorkflows = null)
{
+ var workflows = relatedWorkflows ?? new List();
return new
{
id = record.Id,
@@ -1472,14 +1494,15 @@ namespace NCC.Extend
operateTime = record.SupplementTime?.ToString("yyyy-MM-dd HH:mm:ss"),
workflowId = record.SupplementWorkflowId
},
- leaveApply = string.IsNullOrWhiteSpace(leaveApplyId)
- ? null
- : new
- {
- id = leaveApplyId,
- billNo = leaveApplyBillNo,
- leaveType = leaveApplyType
- },
+ relatedWorkflows = workflows.Select(w => new
+ {
+ w.type,
+ w.id,
+ w.billNo,
+ w.leaveType,
+ w.remark,
+ w.time
+ }).ToList(),
ruleSnapshotJson = record.RuleSnapshotJson,
allRuleSnapshotJson = record.AllRuleSnapshotJson
};
@@ -2263,6 +2286,35 @@ namespace NCC.Extend
return string.IsNullOrWhiteSpace(value) ? null : value.Trim();
}
+ private static List ParseRelatedWorkflows(string json)
+ {
+ if (string.IsNullOrWhiteSpace(json))
+ {
+ return new List();
+ }
+
+ try
+ {
+ return JsonConvert.DeserializeObject>(json) ?? new List();
+ }
+ catch
+ {
+ return new List();
+ }
+ }
+
+ private static string AppendRelatedWorkflow(string existingJson, RelatedWorkflowItem item)
+ {
+ var list = ParseRelatedWorkflows(existingJson);
+ if (!string.IsNullOrWhiteSpace(item.id))
+ {
+ list.RemoveAll(x => x.id == item.id && x.type == item.type);
+ }
+
+ list.Add(item);
+ return JsonConvert.SerializeObject(list);
+ }
+
private sealed class AttendanceStatusContext
{
public int Status { get; set; }
diff --git a/netcore/src/Modularity/WorkFlow/NCC.WorkFlow/WorkFlowForm/LeaveApplyService.cs b/netcore/src/Modularity/WorkFlow/NCC.WorkFlow/WorkFlowForm/LeaveApplyService.cs
index 04840f8..d49d435 100644
--- a/netcore/src/Modularity/WorkFlow/NCC.WorkFlow/WorkFlowForm/LeaveApplyService.cs
+++ b/netcore/src/Modularity/WorkFlow/NCC.WorkFlow/WorkFlowForm/LeaveApplyService.cs
@@ -16,6 +16,7 @@ using NCC.Extend.Entitys.lq_mdxx;
using NCC.FriendlyException;
using NCC.JsonSerialization;
using NCC.System.Entitys.Permission;
+using Newtonsoft.Json;
using NCC.System.Interfaces.System;
using NCC.WorkFlow.Entitys;
using NCC.WorkFlow.Entitys.Dto.WorkFlowForm.LeaveApply;
@@ -883,6 +884,36 @@ LIMIT 1";
}
return count;
}
+
+ ///
+ /// 将新的请假流程项追加到已有的 RelatedWorkflowsJson(去重后返回新 JSON)
+ ///
+ private static string AppendLeaveWorkflow(string existingJson, string newItemArrayJson)
+ {
+ List