Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
J
jilinzhongdianrenqun-web
概览
Overview
Details
Activity
Cycle Analytics
版本库
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
问题
0
Issues
0
列表
Board
标记
里程碑
合并请求
0
Merge Requests
0
CI / CD
CI / CD
流水线
作业
日程表
图表
维基
Wiki
代码片段
Snippets
成员
Members
Collapse sidebar
Close sidebar
活动
图像
聊天
创建新问题
作业
提交
Issue Boards
Open sidebar
吴超
jilinzhongdianrenqun-web
Commits
ee156cd2
Commit
ee156cd2
authored
Dec 30, 2025
by
yubin
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
修改
parent
99f5450d
显示空白字符变更
内嵌
并排
正在显示
2 个修改的文件
包含
235 行增加
和
54 行删除
+235
-54
src/api/key-dm-leave.js
+7
-0
src/view/key-person/key_dm_leave/index.vue
+228
-54
没有找到文件。
src/api/key-dm-leave.js
View file @
ee156cd2
...
...
@@ -124,3 +124,10 @@ export const selectTransferApprovalList = (param) => {
data
:
param
})
}
export
const
deleteById
=
(
param
)
=>
{
return
axios
.
request
({
url
:
'/api/ac/jilinsscgsdp/keyDmLeave/deleteById'
,
method
:
'post'
,
data
:
param
})
}
src/view/key-person/key_dm_leave/index.vue
View file @
ee156cd2
...
...
@@ -7,33 +7,34 @@
<TabPane
label=
"请假申请"
name=
"apply"
>
<div
class=
"search-div"
>
<Row
type=
"flex"
:gutter=
"16"
class=
"mt8"
>
<Col
span=
"1
2
"
>
<Col
span=
"1
8
"
>
<span>
开始:
</span>
<DatePicker
v-model=
"filters.apply.start_time"
type=
"date"
placeholder=
"开始日期"
style=
"
min-width:110px;margin-right:20px
"
/>
<DatePicker
v-model=
"filters.apply.start_time"
type=
"date"
placeholder=
"开始日期"
style=
"
width: 200px"
class=
"mr10
"
/>
<span>
结束:
</span>
<DatePicker
v-model=
"filters.apply.end_time"
type=
"date"
placeholder=
"结束日期"
style=
"min-width:110px;margin-right:20px"
/>
</Col>
<Col
span=
"8"
>
<DatePicker
v-model=
"filters.apply.end_time"
type=
"date"
placeholder=
"结束日期"
style=
"width: 200px"
class=
"mr10"
/>
<span>
状态:
</span>
<Select
v-model=
"filters.apply.status"
style=
"width:
60%
"
>
<Option
v-for=
"opt in statusOptions"
:key=
"opt.id"
:value=
"opt.id"
>
{{
opt
.
name
}}
</Option>
<Select
v-model=
"filters.apply.status"
style=
"width:
200px
"
>
<Option
v-for=
"opt in statusOptions
For('apply')
"
:key=
"opt.id"
:value=
"opt.id"
>
{{
opt
.
name
}}
</Option>
</Select>
</Col>
<Col
span=
"4
"
class=
"text-right"
>
<Col
span=
"6
"
class=
"text-right"
>
<Button
type=
"primary"
class=
"mr10"
@
click=
"handleSearch('apply')"
>
搜索
</Button>
<Button
class=
"mr10"
@
click=
"handleReset('apply')"
>
重置
</Button>
<Button
type=
"
success
"
@
click=
"openApplyModal"
>
申请
</Button>
<Button
type=
"primary"
class=
"mr10"
@
click=
"handleReset('apply')"
>
重置
</Button>
<Button
type=
"
primary
"
@
click=
"openApplyModal"
>
申请
</Button>
</Col>
</Row>
</div>
<Table
border
:loading=
"loading.apply"
:columns=
"applyColumns"
:data=
"tables.apply"
>
<template
slot=
"action"
slot-scope=
"
{ row }">
<Button
size=
"small"
@
click=
"openDetail(row)"
>
详细
</Button>
<Button
size=
"small"
type=
"primary"
class=
"mr5"
@
click=
"openEdit(row)"
v-if=
"canEdit(row)"
>
修改
</Button>
<Button
size=
"small"
type=
"success"
class=
"mr5"
@
click=
"submit(row)"
v-if=
"canSubmit(row)"
>
提交
</Button>
<Button
size=
"small"
type=
"info"
class=
"mr5"
@
click=
"refillApplyDirect(row)"
v-if=
"row.status === -1"
>
重新填报
</Button>
<Button
size=
"small"
class=
"mr10"
@
click=
"openDetail(row)"
>
详细
</Button>
<Button
size=
"small"
type=
"primary"
class=
"mr10"
@
click=
"openEdit(row)"
v-if=
"canEdit(row)"
>
修改
</Button>
<Button
size=
"small"
type=
"success"
class=
"mr10"
@
click=
"submit(row)"
v-if=
"canSubmit(row)"
>
提交
</Button>
<Poptip
confirm
title=
"确认删除?"
transfer
@
on-ok=
"deleteApply(row)"
v-if=
"canDelete(row)"
>
<Button
size=
"small"
type=
"error"
class=
"mr10"
>
删除
</Button>
</Poptip>
<Button
size=
"small"
type=
"primary"
class=
"mr10"
@
click=
"refillApplyDirect(row)"
v-if=
"row.status === -1"
>
重新填报
</Button>
<Poptip
confirm
title=
"确认撤回?"
transfer
@
on-ok=
"revoke(row)"
v-if=
"canRevoke(row)"
>
<Button
size=
"small"
type=
"warning"
>
撤回
</Button>
<Button
size=
"small"
type=
"warning"
class=
"mr10"
>
撤回
</Button>
</Poptip>
</
template
>
</Table>
...
...
@@ -45,7 +46,7 @@
<Table
border
:loading=
"loading.pending"
:columns=
"pendingColumns"
:data=
"tables.pending"
>
<
template
slot=
"action"
slot-scope=
"{ row }"
>
<div
class=
"action-buttons"
>
<Button
size=
"small"
type=
"primary"
@
click=
"openApproveModal(row)"
>
处理
</Button>
<Button
size=
"small"
type=
"primary"
class=
"mr10"
@
click=
"openApproveModal(row)"
>
处理
</Button>
</div>
</
template
>
</Table>
...
...
@@ -56,27 +57,25 @@
<TabPane
label=
"审核历史查询"
name=
"history"
>
<div
class=
"search-div"
>
<Row
type=
"flex"
:gutter=
"16"
align=
"middle"
>
<Col
span=
"1
2
"
>
<Col
span=
"1
8
"
>
<span>
开始:
</span>
<DatePicker
v-model=
"filters.history.start_time"
type=
"date"
placeholder=
"开始日期"
style=
"
min-width:110px;margin-right:20px
"
/>
<DatePicker
v-model=
"filters.history.start_time"
type=
"date"
placeholder=
"开始日期"
style=
"
width: 200px"
class=
"mr10
"
/>
<span>
结束:
</span>
<DatePicker
v-model=
"filters.history.end_time"
type=
"date"
placeholder=
"结束日期"
style=
"min-width:110px;margin-right:20px"
/>
</Col>
<Col
span=
"8"
>
<DatePicker
v-model=
"filters.history.end_time"
type=
"date"
placeholder=
"结束日期"
style=
"width: 200px"
class=
"mr10"
/>
<span>
状态:
</span>
<Select
v-model=
"filters.history.status"
style=
"width:
60%
"
>
<Option
v-for=
"opt in statusOptions"
:key=
"opt.id"
:value=
"opt.id"
>
{{ opt.name }}
</Option>
<Select
v-model=
"filters.history.status"
style=
"width:
200px
"
>
<Option
v-for=
"opt in statusOptions
For('history')
"
:key=
"opt.id"
:value=
"opt.id"
>
{{ opt.name }}
</Option>
</Select>
</Col>
<Col
span=
"
4
"
class=
"text-right"
>
<Col
span=
"
6
"
class=
"text-right"
>
<Button
type=
"primary"
class=
"mr10"
@
click=
"handleSearch('history')"
>
搜索
</Button>
<Button
@
click=
"handleReset('history')"
>
重置
</Button>
<Button
type=
"primary"
class=
"mr10"
@
click=
"handleReset('history')"
>
重置
</Button>
</Col>
</Row>
</div>
<Table
border
:loading=
"loading.history"
:columns=
"historyColumns"
:data=
"tables.history"
>
<
template
slot=
"action"
slot-scope=
"{ row }"
>
<Button
size=
"small"
@
click=
"openHistoryDetail(row)"
>
详细
</Button>
<Button
size=
"small"
class=
"mr10"
@
click=
"openHistoryDetail(row)"
>
详细
</Button>
</
template
>
</Table>
<Page
class=
"page_style"
:total=
"pagers.history.totalRecord"
:current=
"pagers.history.pageNo"
:page-size=
"pagers.history.pageSize"
...
...
@@ -86,27 +85,25 @@
<TabPane
label=
"请假申请查询"
name=
"query"
>
<div
class=
"search-div"
>
<Row
type=
"flex"
:gutter=
"16"
align=
"middle"
>
<Col
span=
"1
2
"
>
<Col
span=
"1
8
"
>
<span>
开始:
</span>
<DatePicker
v-model=
"filters.query.start_time"
type=
"date"
placeholder=
"开始日期"
style=
"
min-width:110px;margin-right:20px
"
/>
<DatePicker
v-model=
"filters.query.start_time"
type=
"date"
placeholder=
"开始日期"
style=
"
width: 200px"
class=
"mr10
"
/>
<span>
结束:
</span>
<DatePicker
v-model=
"filters.query.end_time"
type=
"date"
placeholder=
"结束日期"
style=
"min-width:110px;margin-right:20px"
/>
</Col>
<Col
span=
"8"
>
<DatePicker
v-model=
"filters.query.end_time"
type=
"date"
placeholder=
"结束日期"
style=
"width: 200px"
class=
"mr10"
/>
<span>
状态:
</span>
<Select
v-model=
"filters.query.status"
style=
"width:
60%
"
>
<Option
v-for=
"opt in statusOptions"
:key=
"opt.id"
:value=
"opt.id"
>
{{ opt.name }}
</Option>
<Select
v-model=
"filters.query.status"
style=
"width:
200px
"
>
<Option
v-for=
"opt in statusOptions
For('query')
"
:key=
"opt.id"
:value=
"opt.id"
>
{{ opt.name }}
</Option>
</Select>
</Col>
<Col
span=
"
4
"
class=
"text-right"
>
<Col
span=
"
6
"
class=
"text-right"
>
<Button
type=
"primary"
class=
"mr10"
@
click=
"handleSearch('query')"
>
搜索
</Button>
<Button
@
click=
"handleReset('query')"
>
重置
</Button>
<Button
type=
"primary"
class=
"mr10"
@
click=
"handleReset('query')"
>
重置
</Button>
</Col>
</Row>
</div>
<Table
border
:loading=
"loading.query"
:columns=
"queryColumns"
:data=
"tables.query"
>
<
template
slot=
"action"
slot-scope=
"{ row }"
>
<Button
size=
"small"
@
click=
"openDetail(row)"
>
详细
</Button>
<Button
size=
"small"
class=
"mr10"
@
click=
"openDetail(row)"
>
详细
</Button>
</
template
>
</Table>
<Page
class=
"page_style"
:total=
"pagers.query.totalRecord"
:current=
"pagers.query.pageNo"
:page-size=
"pagers.query.pageSize"
...
...
@@ -116,15 +113,15 @@
<TabPane
label=
"请假统计"
name=
"stats"
>
<div
class=
"search-div"
>
<Row
type=
"flex"
:gutter=
"16"
align=
"middle"
>
<Col
span=
"1
6
"
>
<Col
span=
"1
8
"
>
<span>
开始:
</span>
<DatePicker
v-model=
"statsStart"
type=
"date"
placeholder=
"开始日期"
style=
"
min-width:110px;margin-right:20px
"
/>
<DatePicker
v-model=
"statsStart"
type=
"date"
placeholder=
"开始日期"
style=
"
width: 200px"
class=
"mr10
"
/>
<span>
结束:
</span>
<DatePicker
v-model=
"statsEnd"
type=
"date"
placeholder=
"结束日期"
style=
"
min-width:110px;margin-right:20px
"
/>
<DatePicker
v-model=
"statsEnd"
type=
"date"
placeholder=
"结束日期"
style=
"
width: 200px"
class=
"mr10
"
/>
</Col>
<Col
span=
"
8
"
class=
"text-right"
>
<Col
span=
"
6
"
class=
"text-right"
>
<Button
type=
"primary"
class=
"mr10"
@
click=
"loadStats"
>
统计
</Button>
<Button
@
click=
"resetStats"
>
重置
</Button>
<Button
type=
"primary"
class=
"mr10"
@
click=
"resetStats"
>
重置
</Button>
</Col>
</Row>
</div>
...
...
@@ -146,6 +143,9 @@
<FormItem
label=
"结束时间"
prop=
"end_time"
>
<DatePicker
v-model=
"applyModal.form.end_time"
type=
"datetime"
placeholder=
"请选择结束时间"
/>
</FormItem>
<FormItem
label=
"请假时长(天)"
prop=
"duration"
>
<Input
v-model=
"applyModal.form.duration"
placeholder=
"可手动录入或自动计算"
/>
</FormItem>
<FormItem
label=
"请假事由"
prop=
"reason"
>
<Input
type=
"textarea"
v-model=
"applyModal.form.reason"
:rows=
"3"
/>
</FormItem>
...
...
@@ -155,11 +155,11 @@
<FormItem
label=
"紧急电话"
prop=
"emergency_phone"
>
<Input
v-model=
"applyModal.form.emergency_phone"
/>
</FormItem>
<FormItem
label=
"
指定
审批人"
prop=
"approver_id"
>
<FormItem
label=
"审批人"
prop=
"approver_id"
>
<Select
v-if=
"leaveApprovalOptions.length > 0"
v-model=
"applyModal.form.approver_id"
style=
"width: 60%"
>
<Option
v-for=
"opt in leaveApprovalOptions"
:key=
"opt.id"
:value=
"opt.id"
>
{{ opt.name }}
</Option>
</Select>
<span
v-else
style=
"color: #999; font-style: italic;"
>
暂无审批人
</span>
<span
v-else
style=
"color: #999; font-style: italic;"
>
请先进行审批人配置
</span>
</FormItem>
</Form>
<div
slot=
"footer"
>
...
...
@@ -206,11 +206,14 @@
<Select
v-if=
"leaveApprovalOptions.length > 0"
v-model=
"approveModal.transferApprover"
style=
"width: 60%"
placeholder=
"请选择审批人"
>
<Option
v-for=
"opt in leaveApprovalOptions"
:key=
"opt.id"
:value=
"opt.id"
>
{{ opt.name }}
</Option>
</Select>
<span
v-else
style=
"color: #999; font-style: italic;"
>
暂无审批人
</span>
<span
v-else
style=
"color: #999; font-style: italic;"
>
请先进行审批人配置
</span>
</FormItem>
<FormItem
label=
"审批意见"
prop=
"comment"
>
<Input
type=
"textarea"
v-model=
"approveModal.comment"
:rows=
"3"
placeholder=
"请输入审批意见(驳回时必填)"
/>
<FormItem
:label=
"approveModal.transferEnabled ? '转审原因' : '审批意见'"
prop=
"comment"
>
<Input
type=
"textarea"
v-model=
"approveModal.comment"
:rows=
"3"
:placeholder=
"approveModal.transferEnabled ? '请输入转审原因(选填)' : '请输入审批意见(驳回时必填)'"
/>
</FormItem>
</Form>
<div
slot=
"footer"
>
...
...
@@ -278,8 +281,10 @@ import {
getLeaveStats
,
getLeaveById
,
getLeaveTypeList
,
selectTransferApprovalList
,
transferLeaveApproval
}
from
'@/api/key-dm-leave'
deleteById
,
selectTransferApprovalList
,
transferLeaveApproval
}
from
'@/api/key-dm-leave'
// 注意:不单独调用后台的 isPending 接口,改为复用 getPendingList 获取 totalRecord 判定是否存在待办
import
{
getLeaveApprovalPermission
}
from
'@/api/key-dm-user-permission'
...
...
@@ -311,13 +316,13 @@ export default {
],
applyColumns
:
[
{
type
:
'index'
,
title
:
'序号'
,
width
:
60
,
align
:
'center'
},
{
title
:
'申请人'
,
key
:
'user_name'
,
align
:
'center'
},
{
title
:
'部门'
,
key
:
'department_name'
,
align
:
'center'
},
{
title
:
'申请人'
,
key
:
'user_name'
,
align
:
'center'
},
{
title
:
'请假类型'
,
key
:
'leave_type_name'
,
align
:
'center'
},
{
title
:
'起止时间'
,
key
:
'start_time'
,
width
:
300
,
align
:
'center'
,
render
:
(
h
,
{
row
})
=>
h
(
'span'
,
`
${
row
.
start_time
||
'-'
}
~
${
row
.
end_time
||
'-'
}
`
)
},
{
title
:
'时长'
,
key
:
'duration'
,
align
:
'center'
},
{
title
:
'状态'
,
key
:
'status'
,
align
:
'center'
,
render
:
(
h
,
{
row
})
=>
h
(
'span'
,
this
.
mapStatusText
(
row
.
status
))
},
{
title
:
'操作'
,
slot
:
'action'
,
align
:
'center'
,
width
:
16
0
}
{
title
:
'操作'
,
slot
:
'action'
,
align
:
'center'
,
width
:
25
0
}
],
pendingColumns
:
[
{
type
:
'index'
,
title
:
'序号'
,
width
:
60
,
align
:
'center'
},
...
...
@@ -373,6 +378,8 @@ export default {
}
],
applyModal
:
{
visible
:
false
,
isEdit
:
false
,
saving
:
false
,
form
:
{}
},
// 控制是否在赋值时触发自动计算(true = 允许计算;false = 赋值时不计算)
suppressDurationCalc
:
false
,
approveModal
:
{
visible
:
false
,
record
:
{},
comment
:
''
,
submitting
:
false
,
transferEnabled
:
false
,
transferApprover
:
''
,
approvalFlow
:
[]
},
transferModal
:
{
visible
:
false
,
selectedApprover
:
''
,
comment
:
''
,
processing
:
false
,
record
:
{}
},
detailModal
:
{
visible
:
false
,
loading
:
false
,
data
:
{},
approvals
:
[]
},
...
...
@@ -390,8 +397,59 @@ export default {
end_time
:
[
{
required
:
true
,
message
:
'请选择结束时间'
,
trigger
:
'change'
,
type
:
'date'
}
],
duration
:
[
{
validator
:
function
(
rule
,
value
,
callback
)
{
const
form
=
(
this
&&
this
.
applyModal
&&
this
.
applyModal
.
form
)
?
this
.
applyModal
.
form
:
{}
const
parseToDateLocal
=
(
v
)
=>
{
if
(
v
===
null
||
v
===
undefined
||
v
===
''
)
return
null
if
(
typeof
v
===
'number'
)
return
new
Date
(
v
)
if
(
typeof
v
===
'string'
)
{
if
(
/^
\d
+$/
.
test
(
v
))
return
new
Date
(
Number
(
v
))
const
s
=
v
.
includes
(
'T'
)
?
v
:
v
.
replace
(
' '
,
'T'
)
const
parsed
=
new
Date
(
s
)
return
isNaN
(
parsed
.
getTime
())
?
null
:
parsed
}
if
(
v
instanceof
Date
)
return
isNaN
(
v
.
getTime
())
?
null
:
v
const
parsed
=
new
Date
(
v
)
return
isNaN
(
parsed
.
getTime
())
?
null
:
parsed
}
// 解析起止时间的整天差
const
sd
=
parseToDateLocal
(
form
.
start_time
)
const
ed
=
parseToDateLocal
(
form
.
end_time
)
let
fullDays
=
0
if
(
sd
&&
ed
)
{
const
diff
=
ed
.
getTime
()
-
sd
.
getTime
()
fullDays
=
Math
.
floor
(
diff
/
(
1000
*
60
*
60
*
24
))
}
// 如果不足整天,不校验(允许用户填写任意时长)
if
(
fullDays
<
1
)
{
callback
();
return
}
// 整天或以上时,必须填写且整数部分需与计算天数一致
if
(
value
===
null
||
value
===
undefined
||
String
(
value
).
trim
()
===
''
)
{
callback
(
new
Error
(
'请填写请假时长'
));
return
}
const
num
=
Number
(
value
)
if
(
isNaN
(
num
)
||
num
<=
0
)
{
callback
(
new
Error
(
'请假天数必须为大于 0 的数字'
));
return
}
const
integerPart
=
Math
.
floor
(
num
)
if
(
integerPart
!==
fullDays
)
{
callback
(
new
Error
(
`请假整数天应为
${
fullDays
}
天,请确认`
));
return
}
callback
()
},
trigger
:
'blur'
}
],
approver_id
:
[
{
required
:
true
,
message
:
'请选择指定审批人'
,
trigger
:
'change'
}
{
required
:
true
,
message
:
'请选择审批人'
,
trigger
:
'change'
}
],
reason
:
[
{
required
:
true
,
message
:
'请填写请假事由'
,
trigger
:
'change'
}
],
emergency_phone
:
[
{
message
:
'请输入紧急电话'
,
trigger
:
'blur'
},
...
...
@@ -400,7 +458,8 @@ export default {
message
:
'请输入有效的电话号码'
,
trigger
:
'blur'
}
]
],
// (必填性与具体数值由上面的 validator 决定)
}
}
},
...
...
@@ -410,8 +469,23 @@ export default {
this
.
loadLeaveApprovalOptions
()
// 加载待审批计数,用于页面顶部/选项卡徽章显示
this
.
loadPendingCount
()
// 将 duration 的 validator 绑定到组件实例的方法,确保内部能访问 this.applyModal.form
this
.
applyRules
.
duration
=
[{
validator
:
this
.
validateDuration
,
trigger
:
'blur'
}]
},
watch
:
{
// 监听开始时间和结束时间变化,自动计算天数
'applyModal.form.start_time'
(
newVal
)
{
if
(
this
.
suppressDurationCalc
)
return
// 用户修改起始时间时,先清空已有时长(避免与新时间不符),再进行自动计算
if
(
this
.
applyModal
&&
this
.
applyModal
.
form
)
this
.
applyModal
.
form
.
duration
=
''
this
.
calculateDuration
()
},
'applyModal.form.end_time'
(
newVal
)
{
if
(
this
.
suppressDurationCalc
)
return
// 用户修改结束时间时,先清空已有时长(避免与新时间不符),再进行自动计算
if
(
this
.
applyModal
&&
this
.
applyModal
.
form
)
this
.
applyModal
.
form
.
duration
=
''
this
.
calculateDuration
()
},
// 当弹窗关闭时清理选择的审批人,确保下次打开时下拉框能正确刷新显示
'applyModal.visible'
(
val
)
{
if
(
!
val
&&
this
.
applyModal
&&
this
.
applyModal
.
form
)
{
...
...
@@ -439,6 +513,79 @@ export default {
}
},
methods
:
{
// validator 方法:确保能通过 this 访问组件数据
validateDuration
(
rule
,
value
,
callback
)
{
const
form
=
this
.
applyModal
&&
this
.
applyModal
.
form
?
this
.
applyModal
.
form
:
{}
const
parseToDateLocal
=
(
v
)
=>
{
if
(
v
===
null
||
v
===
undefined
||
v
===
''
)
return
null
if
(
typeof
v
===
'number'
)
return
new
Date
(
v
)
if
(
typeof
v
===
'string'
)
{
if
(
/^
\d
+$/
.
test
(
v
))
return
new
Date
(
Number
(
v
))
const
s
=
v
.
includes
(
'T'
)
?
v
:
v
.
replace
(
' '
,
'T'
)
const
parsed
=
new
Date
(
s
)
return
isNaN
(
parsed
.
getTime
())
?
null
:
parsed
}
if
(
v
instanceof
Date
)
return
isNaN
(
v
.
getTime
())
?
null
:
v
const
parsed
=
new
Date
(
v
)
return
isNaN
(
parsed
.
getTime
())
?
null
:
parsed
}
const
sd
=
parseToDateLocal
(
form
.
start_time
)
const
ed
=
parseToDateLocal
(
form
.
end_time
)
let
fullDays
=
0
if
(
sd
&&
ed
)
{
const
diff
=
ed
.
getTime
()
-
sd
.
getTime
()
fullDays
=
Math
.
floor
(
diff
/
(
1000
*
60
*
60
*
24
))
}
// 如果不足整天,不校验
if
(
fullDays
<
1
)
{
callback
();
return
}
if
(
value
===
null
||
value
===
undefined
||
String
(
value
).
trim
()
===
''
)
{
callback
(
new
Error
(
'请填写请假时长'
));
return
}
const
num
=
Number
(
value
)
if
(
isNaN
(
num
)
||
num
<=
0
)
{
callback
(
new
Error
(
'请假天数必须为大于 0 的数字'
));
return
}
const
integerPart
=
Math
.
floor
(
num
)
if
(
integerPart
!==
fullDays
)
{
callback
(
new
Error
(
`请假整数天应为
${
fullDays
}
天,请确认`
));
return
}
callback
()
},
// 返回给定 tab 的状态下拉选项:只有 apply tab 显示“未提交(0)”,其他 tab 隐藏该选项
statusOptionsFor
(
tab
)
{
if
(
tab
===
'apply'
)
return
this
.
statusOptions
// 过滤掉 id 为 '0' 或 0 的未提交选项
return
(
this
.
statusOptions
||
[]).
filter
(
opt
=>
String
(
opt
.
id
)
!==
'0'
)
},
// 计算请假天数(只计算整天,忽略零点几部分)
calculateDuration
()
{
const
form
=
this
.
applyModal
.
form
if
(
!
form
||
!
form
.
start_time
||
!
form
.
end_time
)
return
const
startDate
=
new
Date
(
form
.
start_time
)
const
endDate
=
new
Date
(
form
.
end_time
)
if
(
isNaN
(
startDate
.
getTime
())
||
isNaN
(
endDate
.
getTime
()))
return
if
(
endDate
.
getTime
()
<=
startDate
.
getTime
())
return
// 计算整天数(向下取整),只要整天 >=1 则自动填充为整数天
const
diffTime
=
endDate
.
getTime
()
-
startDate
.
getTime
()
const
fullDays
=
Math
.
floor
(
diffTime
/
(
1000
*
60
*
60
*
24
))
const
existing
=
form
.
duration
!==
null
&&
form
.
duration
!==
undefined
&&
String
(
form
.
duration
).
trim
()
!==
''
if
(
fullDays
>=
1
)
{
// 只有当表单中尚未填写时才自动填充,避免覆盖后端/用户已有值
if
(
!
existing
)
form
.
duration
=
String
(
fullDays
)
}
else
{
// 少于整天时不自动计算,交由用户手动填写(例如半天、几小时)
// 不覆盖已有用户填写的值
if
(
!
existing
)
form
.
duration
=
''
}
},
handleTabChange
(
name
)
{
this
.
activeTab
=
name
if
(
name
===
'pending'
)
this
.
fetchList
(
'pending'
)
...
...
@@ -709,12 +856,18 @@ export default {
// 默认开始时间为当天(时分秒置为 0),精确到天
const
today
=
new
Date
()
today
.
setHours
(
0
,
0
,
0
,
0
)
this
.
applyModal
.
form
=
{
user_id
:
''
,
user_name
:
''
,
leave_type_id
:
''
,
start_time
:
today
,
end_time
:
''
,
duration_unit
:
2
,
duration
:
0
,
reason
:
''
,
emergency_contact
:
''
,
emergency_phone
:
''
,
approver_id
:
this
.
selectedApprovalUser
||
''
}
// 在赋值期间禁止自动计算,直到下一个 tick 再恢复,避免渲染期间 watcher 覆盖表单值
this
.
suppressDurationCalc
=
true
this
.
applyModal
.
form
=
{
user_id
:
''
,
user_name
:
''
,
leave_type_id
:
''
,
start_time
:
today
,
end_time
:
''
,
duration_unit
:
2
,
duration
:
''
,
reason
:
''
,
emergency_contact
:
''
,
emergency_phone
:
''
,
approver_id
:
this
.
selectedApprovalUser
||
''
}
this
.
$nextTick
(()
=>
{
this
.
suppressDurationCalc
=
false
})
this
.
applyModal
.
visible
=
true
},
openEdit
(
row
)
{
this
.
applyModal
.
isEdit
=
true
// 在赋值期间禁止自动计算,直到渲染稳定后再恢复,避免覆盖已有 duration
this
.
suppressDurationCalc
=
true
this
.
applyModal
.
form
=
Object
.
assign
({},
row
)
this
.
$nextTick
(()
=>
{
this
.
suppressDurationCalc
=
false
})
// ensure approver_id exists on form when editing
if
(
!
this
.
applyModal
.
form
.
approver_id
)
this
.
applyModal
.
form
.
approver_id
=
row
.
approver_id
||
row
.
approverId
||
row
.
transfer_to_id
||
''
// 保证与 leaveApprovalOptions 中 id 的类型一致(字符串)
...
...
@@ -788,6 +941,10 @@ export default {
const
status
=
row
&&
row
.
status
!==
null
&&
row
.
status
!==
undefined
?
Number
(
row
.
status
)
:
null
return
status
===
1
},
canDelete
(
row
)
{
const
status
=
row
&&
row
.
status
!==
null
&&
row
.
status
!==
undefined
?
Number
(
row
.
status
)
:
null
return
status
===
0
},
submit
(
row
)
{
this
.
$Modal
.
confirm
({
title
:
'确认提交'
,
content
:
'确认提交此请假申请进入审批流程?'
,
...
...
@@ -837,7 +994,7 @@ export default {
}
}).
catch
(
err
=>
{
// 出错时提供默认占位并清空展示
this
.
transferApprovalOptions
=
[{
id
:
''
,
name
:
'
暂无审批人
'
}]
this
.
transferApprovalOptions
=
[{
id
:
''
,
name
:
'
请先进行审批人配置
'
}]
this
.
approveModal
.
approvalFlow
=
[]
console
.
error
(
'selectTransferApprovalList error'
,
err
)
}).
finally
(()
=>
{
...
...
@@ -933,6 +1090,8 @@ export default {
refillApply
()
{
// 将批准记录数据反显到申请表单,清除 id 和 leave_id
this
.
applyModal
.
isEdit
=
false
// 在赋值期间禁止自动计算,直到渲染稳定后再恢复
this
.
suppressDurationCalc
=
true
this
.
applyModal
.
form
=
{
leave_type_id
:
this
.
approveModal
.
record
.
leave_type_id
||
''
,
start_time
:
this
.
approveModal
.
record
.
start_time
||
''
,
...
...
@@ -943,6 +1102,7 @@ export default {
approver_id
:
this
.
approveModal
.
record
.
approver_id
||
this
.
selectedApprovalUser
||
''
}
// 不保留 id 和 leave_id,保持为空
this
.
$nextTick
(()
=>
{
this
.
suppressDurationCalc
=
false
})
this
.
applyModal
.
visible
=
true
// 关闭审批弹窗
this
.
approveModal
.
visible
=
false
...
...
@@ -950,6 +1110,8 @@ export default {
refillApplyDirect
(
row
)
{
// 直接从列表行数据进行重新填报,清除 id 和 leave_id
this
.
applyModal
.
isEdit
=
false
// 在赋值期间禁止自动计算,直到渲染稳定后再恢复
this
.
suppressDurationCalc
=
true
this
.
applyModal
.
form
=
{
leave_type_id
:
row
.
leave_type_id
||
''
,
start_time
:
row
.
start_time
||
''
,
...
...
@@ -960,6 +1122,7 @@ export default {
approver_id
:
row
.
approver_id
||
this
.
selectedApprovalUser
||
''
}
// 不保留 id 和 leave_id,保持为空
this
.
$nextTick
(()
=>
{
this
.
suppressDurationCalc
=
false
})
this
.
applyModal
.
visible
=
true
},
openDetail
(
row
)
{
...
...
@@ -1024,6 +1187,17 @@ export default {
this
.
statsEnd
=
null
this
.
statsDept
=
''
this
.
tables
.
stats
=
[]
},
deleteApply
(
row
)
{
deleteById
({
id
:
row
.
id
,
leave_id
:
row
.
leave_id
}).
then
(
ret
=>
{
if
(
ret
.
data
&&
ret
.
data
.
errcode
===
0
)
{
this
.
$Message
.
success
(
'删除成功'
)
this
.
fetchList
(
'apply'
)
}
else
{
this
.
$Notice
.
error
({
title
:
'删除失败'
,
desc
:
ret
.
data
&&
ret
.
data
.
errmsg
})
}
})
}
}
}
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论