Commit 64b56af8 by zhangtw
parents 56bce0bb 87e44d35
......@@ -100,7 +100,7 @@ export const getLeaveStats = (param) => {
export const getLeaveTypeList = (param) => {
return axios.request({
url: '/api/ac/jilinsscgsdp/keyDmLeaveType/selectList',
url: '/api/ac/jilinsscgsdp/keyDmUserCategory/selectLeaveTypeList',
method: 'post',
data: param
})
......@@ -114,3 +114,12 @@ export const transferLeaveApproval = (param) => {
data: param
})
}
// 选择转审核审批人列表
export const selectTransferApprovalList = (param) => {
return axios.request({
url: '/api/ac/jilinsscgsdp/keyDmLeave/selectTransferApprovalList',
method: 'post',
data: param
})
}
......@@ -2,52 +2,52 @@ import axios from '@/libs/api.request'
const permissionType = {
// 键:枚举名,值:枚举值(可以是数字/字符串)
LEAVEAPPROVAL: "leave_approval", // 请假审核
LEAVEVIEW: "leave_view", // 请假查询统计
SUPPLYAPPROVAL: "supply_approval", // 用品审核
SUPPLYVIEW: "supply_view" // 用品查询统计
};
LEAVEAPPROVAL: 'leave_approval', // 请假审核
LEAVEVIEW: 'leave_view', // 请假查询统计
SUPPLYAPPROVAL: 'supply_approval', // 用品审核
SUPPLYVIEW: 'supply_view' // 用品查询统计
}
// 获取日常人员列表
export const getLeaveApprovalPermission = () => {
return axios.request({
url: '/api/ac/jilinsscgsdp/keyDmUserCategoryPermission/selectOrgPermission',
url: '/api/ac/jilinsscgsdp/keyDmUserCategory/selectOrgPermission',
method: 'post',
data:{
data: {
permissionType: permissionType.LEAVEAPPROVAL
}
})
}
}
// 根据机构编码同步本级及下属机构用户(存在则更新、不存在则新增;null 不覆盖)
export const getLeaveView = () => {
// 根据机构编码同步本级及下属机构用户(存在则更新、不存在则新增;null 不覆盖)
export const getLeaveView = () => {
return axios.request({
url: '/api/ac/jilinsscgsdp/keyDmUserCategoryPermission/selectOrgPermission',
url: '/api/ac/jilinsscgsdp/keyDmUserCategory/selectOrgPermission',
method: 'post',
data:{
data: {
permissionType: permissionType.LEAVEVIEW
}
})
}
}
// 保存单个日常人员(前端编辑后保存)
export const getSupplyApproval = () => {
// 保存单个日常人员(前端编辑后保存)
export const getSupplyApproval = () => {
return axios.request({
url: '/api/ac/jilinsscgsdp/keyDmUserCategoryPermission/selectOrgPermission',
url: '/api/ac/jilinsscgsdp/keyDmUserCategory/selectOrgPermission',
method: 'post',
data:{
data: {
permissionType: permissionType.SUPPLYAPPROVAL
}
})
}
}
// 分页查询日常人员可选上级领导列表
export const getSupplyView = () => {
// 分页查询日常人员可选上级领导列表
export const getSupplyView = () => {
return axios.request({
url: '/api/ac/jilinsscgsdp/keyDmUserCategoryPermission/selectOrgPermission',
method: 'post',
data:{
data: {
permissionType: permissionType.SUPPLYVIEW
}
})
}
\ No newline at end of file
}
......@@ -47,7 +47,7 @@ export const dmUserOffice = (param) => {
export const getUserTypeList = (param) => {
return axios.request({
url: '/api/ac/jilinsscgsdp/keyDmUserCategory/selectList',
url: '/api/ac/jilinsscgsdp/keyDmUserCategory/selectList'
})
}
......
......@@ -3,18 +3,20 @@
<Tabs v-model="activeTab" @on-click="handleTabChange" class="tabsCls">
<TabPane label="请假申请" name="apply">
<div class="search-div">
<Row type="flex" :gutter="16">
<Col span="8">
<span>姓名:</span>
<Input v-model="filters.apply.name" placeholder="请输入姓名" style="width: 60%" />
<Row type="flex" :gutter="16" class="mt8">
<Col span="12">
<span>开始:</span>
<DatePicker v-model="filters.apply.start_time" type="date" placeholder="开始日期" style="min-width:110px;margin-right:20px" />
<span>结束:</span>
<DatePicker v-model="filters.apply.end_time" type="date" placeholder="结束日期" style="min-width:110px;margin-right:20px" />
</Col>
<Col span="8">
<span>审批人</span>
<Select v-model="selectedApprovalUser" style="width: 60%">
<Option v-for="opt in leaveApprovalOptions" :key="opt.id" :value="opt.id">{{ opt.name }}</Option>
<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>
</Col>
<Col span="8" class="text-right">
<Col span="4" 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>
......@@ -26,6 +28,7 @@
<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>
<Poptip confirm title="确认撤回?" transfer @on-ok="revoke(row)" v-if="canRevoke(row)">
<Button size="small" type="warning">撤回</Button>
</Poptip>
......@@ -40,7 +43,6 @@
<template slot="action" slot-scope="{ row }">
<div class="action-buttons">
<Button size="small" type="primary" @click="openApproveModal(row)">处理</Button>
<Button size="small" class="mr5" @click="openTransferModal(row)" v-if="canTransfer(row)">转审核</Button>
</div>
</template>
</Table>
......@@ -49,6 +51,26 @@
</TabPane>
<TabPane label="审核历史查询" name="history">
<div class="search-div">
<Row type="flex" :gutter="16" align="middle">
<Col span="12">
<span>开始:</span>
<DatePicker v-model="filters.history.start_time" type="date" placeholder="开始日期" style="min-width:110px;margin-right:20px" />
<span>结束:</span>
<DatePicker v-model="filters.history.end_time" type="date" placeholder="结束日期" style="min-width:110px;margin-right:20px" />
</Col>
<Col span="8">
<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>
</Col>
<Col span="4" class="text-right">
<Button type="primary" class="mr10" @click="handleSearch('history')">搜索</Button>
<Button @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>
......@@ -59,6 +81,26 @@
</TabPane>
<TabPane label="请假申请查询" name="query">
<div class="search-div">
<Row type="flex" :gutter="16" align="middle">
<Col span="8">
<span>开始:</span>
<DatePicker v-model="filters.query.start_time" type="date" placeholder="开始日期" style="min-width:110px;margin-right:20px" />
<span>结束:</span>
<DatePicker v-model="filters.query.end_time" type="date" placeholder="结束日期" style="min-width:110px;margin-right:20px" />
</Col>
<Col span="8">
<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>
</Col>
<Col span="8" class="text-right">
<Button type="primary" class="mr10" @click="handleSearch('query')">搜索</Button>
<Button @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>
......@@ -113,9 +155,10 @@
<Input v-model="applyModal.form.emergency_phone" />
</FormItem>
<FormItem label="指定审批人" prop="approver_id">
<Select v-model="applyModal.form.approver_id" style="width: 60%">
<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>
</FormItem>
</Form>
<div slot="footer">
......@@ -139,13 +182,41 @@
<FormItem label="请假事由">
<span>{{ approveModal.record.reason }}</span>
</FormItem>
<!-- 审批流程 -->
<FormItem v-if="approveModal.approvalFlow && approveModal.approvalFlow.length > 0" label="审批流程">
<div class="approval-flow-section">
<div v-for="(flow, index) in approveModal.approvalFlow" :key="index" class="flow-item">
<Row class="flow-row">
<Col span="12" class="flow-col"><span class="label">审核人:</span><span class="value no-ellipsis" :title="flow.approver_name">{{ flow.approver_name || '-' }}</span></Col>
<Col span="12" class="flow-col"><span class="label">时间:</span><span class="value no-ellipsis" :title="flow.create_time">{{ flow.create_time || '-' }}</span></Col>
</Row>
<Row class="flow-row">
<Col span="24" class="flow-col"><span class="label">转审核意见:</span><span class="value" :title="flow.transfer_reason">{{ flow.transfer_reason || '-' }}</span></Col>
</Row>
</div>
</div>
</FormItem>
<FormItem>
<Checkbox v-model="approveModal.transferEnabled">转审核</Checkbox>
</FormItem>
<FormItem v-if="approveModal.transferEnabled" label="选择审批人" prop="transferApprover">
<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>
</FormItem>
<FormItem label="审批意见" prop="comment">
<Input type="textarea" v-model="approveModal.comment" :rows="3" placeholder="请输入审批意见(驳回时必填)" />
</FormItem>
</Form>
<div slot="footer">
<Button @click="approveModal.visible = false">取消</Button>
<Button type="success" :loading="approveModal.submitting" @click="confirmApprove">通过</Button>
<Button type="info" @click="refillApply" v-if="approveModal.record.status === -1">重新填报</Button>
<Button type="success" :loading="approveModal.submitting" @click="confirmApprove" v-if="!approveModal.transferEnabled">通过</Button>
<Button type="primary" :loading="approveModal.submitting" @click="confirmTransferApprove" v-if="approveModal.transferEnabled">确认</Button>
<Button type="error" :loading="approveModal.submitting" @click="confirmReject">驳回</Button>
</div>
</Modal>
......@@ -161,6 +232,8 @@
<Row class="mt8"><Col span="24"><p><strong>请假类型:</strong>{{ detailModal.data.leave_type_name || '-' }}</p></Col></Row>
<Row class="mt8"><Col span="24"><p><strong>起止时间:</strong>{{ formatDatetime(detailModal.data.start_time) }} - {{ formatDatetime(detailModal.data.end_time) }}</p></Col></Row>
<Row class="mt8"><Col span="24"><p><strong>请假事由:</strong>{{ detailModal.data.reason || '-' }}</p></Col></Row>
<Row class="mt8"><Col span="24"><p><strong>状态:</strong>{{ mapStatusText(detailModal.data.status) }}</p></Col></Row>
<Row v-if="detailModal.data.status === -1 || String(detailModal.data.status) === '-1'" class="mt8"><Col span="24"><p><strong>驳回理由:</strong>{{ detailModal.data.back_reason || detailModal.data.backReason || '-' }}</p></Col></Row>
<Row class="mt8"><Col span="24"><h4>审批记录</h4></Col></Row>
<div>
<!-- 详情表格:使用预先定义的列配置渲染 approvals -->
......@@ -203,9 +276,9 @@ import {
getApprovalHistory,
getLeaveStats,
getLeaveById,
getLeaveTypeList
} from '@/api/key-dm-leave'
import { transferLeaveApproval } from '@/api/key-dm-leave'
getLeaveTypeList,
selectTransferApprovalList
, transferLeaveApproval } from '@/api/key-dm-leave'
import { getLeaveApprovalPermission } from '@/api/key-dm-user-permission'
export default {
......@@ -213,14 +286,25 @@ export default {
data () {
return {
activeTab: 'apply',
filters: { apply: { name: '' } },
filters: { apply: { start_time: null, end_time: null, status: '' }, history: { start_time: null, end_time: null, status: '' }, query: { start_time: null, end_time: null, status: '' } },
// 下拉:请假审核权限可选人员
leaveApprovalOptions: [],
// 下拉:转审核审批人列表
transferApprovalOptions: [],
selectedApprovalUser: '',
tables: { apply: [], pending: [], history: [], query: [], stats: [] },
pagers: { apply: { pageNo: 1, pageSize: 10, totalRecord: 0 }, pending: { pageNo: 1, pageSize: 10, totalRecord: 0 }, history: { pageNo: 1, pageSize: 10, totalRecord: 0 }, query: { pageNo: 1, pageSize: 10, totalRecord: 0 } },
loading: { apply: false, pending: false, history: false, query: false, stats: false },
leaveTypes: [],
// 状态下拉
statusOptions: [
{ id: '', name: '全部' },
{ id: '0', name: '未提交' },
{ id: '1', name: '待审批' },
{ id: '2', name: '审批中' },
{ id: '9', name: '审核通过' },
{ id: '-1', name: '已驳回' }
],
applyColumns: [
{ type: 'index', title: '序号', width: 60, align: 'center' },
{ title: '申请人', key: 'user_name', align: 'center' },
......@@ -243,7 +327,7 @@ export default {
{ type: 'index', title: '序号', width: 60, align: 'center' },
{ title: '申请人', key: 'user_name', align: 'center' },
{ title: '请假类型', key: 'leave_type_name', align: 'center' },
{ title: '起止时间', key: 'start_time', align: 'center' },
{ title: '起止时间', key: 'start_time', align: 'center', render: (h, { row }) => h('span', `${row.start_time || '-'} ~ ${row.end_time || '-'}`) },
{ title: '审批完成时间', key: 'approval_complete_time', align: 'center' },
{ title: '操作', slot: 'action', align: 'center', width: 100 }
],
......@@ -261,14 +345,31 @@ export default {
{ title: '已通过天数', key: 'passed_days', align: 'center' }
],
detailApprovalColumns: [
{ title: '节点', key: 'node_index', align: 'center' },
{
title: '审批类型',
key: 'node_type',
align: 'center',
render: (h, { row }) => {
const v = row && (row.node_type || row.nodeType)
const text = (v === 1 || String(v) === '1') ? '流程' : ((v === 2 || String(v) === '2') ? '转审' : (v || '-'))
return h('span', text)
}
},
{ title: '审批人', key: 'approver_name', align: 'center' },
{ title: '结果', key: 'approver_result', align: 'center', render: (h, { row }) => h('span', this.renderApproveResult(h, { row })) },
{ title: '时间', key: 'approver_time', align: 'center' },
{ title: '意见', key: 'comment', align: 'center' }
{
title: '进度',
key: 'status',
align: 'center',
render: (h, { row }) => {
const s = row && (row.status === 0 || row.status === '0' ? 0 : (row.status || row.approval_status || ''))
const text = (s === 1 || String(s) === '1') ? '处理中' : ((s === 2 || String(s) === '2') ? '已处理' : ((s === 0 || String(s) === '0') ? '未处理' : '-'))
return h('span', text)
}
},
],
applyModal: { visible: false, isEdit: false, saving: false, form: {} },
approveModal: { visible: false, record: {}, comment: '', submitting: 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: [] },
statsStart: new Date(new Date().getFullYear(), new Date().getMonth(), 1),
......@@ -281,6 +382,33 @@ export default {
this.fetchList('apply')
this.loadLeaveApprovalOptions()
},
watch: {
// 当弹窗关闭时清理选择的审批人,确保下次打开时下拉框能正确刷新显示
'applyModal.visible' (val) {
if (!val && this.applyModal && this.applyModal.form) {
// 仅在新增模式(isEdit 为 false)时清空审批人,编辑模式保留选择
if (!this.applyModal.isEdit) {
this.applyModal.form.approver_id = ''
}
}
},
'transferModal.visible' (val) {
if (!val && this.transferModal) {
this.transferModal.selectedApprover = ''
this.transferModal.comment = ''
}
},
'approveModal.visible' (val) {
if (!val && this.approveModal) {
// 关闭审批弹窗时清空审批流程和转审核相关字段,避免残留数据影响下次打开
this.approveModal.approvalFlow = []
this.approveModal.transferEnabled = false
this.approveModal.transferApprover = ''
this.approveModal.comment = ''
this.approveModal.record = {}
}
}
},
methods: {
handleTabChange (name) {
this.activeTab = name
......@@ -293,7 +421,11 @@ export default {
const api = apiMap[tab]
if (!api) return
this.loading[tab] = true
const payload = Object.assign({}, this.pagers[tab] || {}, { params: this.filters[tab] || {} })
// 在发送请求前把 start_time/end_time 格式化为 DB 可接受的字符串(并设置为当天边界)
// Send formatted query params directly under `params`.
// Some HTTP wrapper will place this object into the server-side `map.params`,
// so avoid adding an extra `params` nesting here.
const payload = Object.assign({}, this.pagers[tab] || {}, { params: this.prepareQueryParams(tab) })
api(payload).then(ret => {
if (ret.data && ret.data.errcode === 0) {
const data = ret.data.data || {}
......@@ -306,9 +438,20 @@ export default {
if (Object.prototype.hasOwnProperty.call(item, 'end_time')) {
item.end_time = this.formatDisplayDatetime(item.end_time)
}
if (Object.prototype.hasOwnProperty.call(item, 'submit_time')) {
item.submit_time = this.formatDisplayDatetime(item.submit_time)
}
if (Object.prototype.hasOwnProperty.call(item, 'approval_complete_time')) {
item.approval_complete_time = this.formatDisplayDatetime(item.approval_complete_time)
}
// Normalize approver id/name from various backend key possibilities so the edit modal can reflect the selected approver
item.approver_id = item.approver_id || item.approverId || item.transfer_to_id || item.transferToId || item.deal_user_id || item.dealUserId || ''
item.approver_name = item.approver_name || item.approverName || item.transfer_to_name || item.transferToName || item.deal_user_name || item.dealUserName || item.update_name || item.update_by_name || ''
// 仅以 approver_name / approverName 为准,不使用 transfer_to_name 回退,避免混淆审批人来源
item.approver_name = item.approver_name || item.approverName || item.deal_user_name || item.dealUserName || item.update_name || item.update_by_name || ''
// Normalize transfer reason from various backend key possibilities
item.transfer_reason = item.transfer_reason || item.transferReason || item.reason || ''
// Normalize leave_id (主表id) from various backend key possibilities
item.leave_id = item.leave_id || item.leaveId || item.main_id || item.mainId || ''
return item
})
if (tab === 'history') this.tables.history = normalizedResults
......@@ -322,15 +465,55 @@ export default {
}).finally(() => { this.loading[tab] = false })
},
/**
* Prepare query params: normalize start_time/end_time into DB datetime strings.
* - If start_time exists, set to 00:00:00 of that day.
* - If end_time exists, set to 23:59:59 of that day.
* Returns a shallow copy of filters[tab] with formatted strings.
*/
prepareQueryParams (tab) {
const raw = (this.filters && this.filters[tab]) ? Object.assign({}, this.filters[tab]) : {}
const parseToDate = (v) => {
if (v === null || v === undefined || v === '') return null
if (v instanceof Date) return isNaN(v.getTime()) ? null : new Date(v)
try {
// Handles ISO strings and numeric timestamps as well
const d = new Date(v)
return isNaN(d.getTime()) ? null : d
} catch (e) { return null }
}
if (raw.start_time) {
const sd = parseToDate(raw.start_time)
if (sd) {
sd.setHours(0, 0, 0, 0)
raw.start_time = this.formatForDbDatetime(sd)
}
}
// 只有当用户选择了结束时间时,才设置结束时间的查询条件
// 如果没有选择结束时间,则不设置 end_time,让后端查询从开始时间之后的所有记录
if (raw.end_time) {
const ed = parseToDate(raw.end_time)
if (ed) {
ed.setHours(23, 59, 59, 0)
raw.end_time = this.formatForDbDatetime(ed)
}
} else {
// 如果用户没有选择结束时间,删除 end_time 属性,确保后端不按结束时间过滤
delete raw.end_time
}
return raw
},
/**
* Map numeric approval status codes to human-readable text.
* 0 => 未审批
* 1 => 审核中
* 0 => 未提交
* 1 => 待审批
* 2 => 审批中
* 9 => 审核通过
* other => 显示原始值或通用占位
*/
mapStatusText (status) {
if (status === 0 || String(status) === '0') return '未审批'
if (status === 1 || String(status) === '1') return '审核中'
if (status === 0 || String(status) === '0') return '未提交'
if (status === 1 || String(status) === '1') return '待审批'
if (status === 2 || String(status) === '2') return '审批中'
if (status === 9 || String(status) === '9') return '审核通过'
if (status === -1 || String(status) === '-1') return '已驳回'
if (status === null || status === undefined || status === '') return '-'
......@@ -355,11 +538,14 @@ export default {
// Map results into the shape used by the select
this.leaveTypes = results.map(it => ({ id: it.id, type_name: it.type_name }))
// 如果没有数据,添加默认选项
if (this.leaveTypes.length === 0) {
this.leaveTypes = [{ id: '', type_name: '暂无请假类型' }]
}
}).catch(err => {
// 保持简洁的错误输出,避免抛出未捕获异常
console.error('loadLeaveTypes error', err)
})
},
loadLeaveApprovalOptions () {
// 从后端获取有请假审批权限的机构/人员列表,并映射为 { id, name } 供 Select 使用
......@@ -373,9 +559,11 @@ export default {
else if (d.data && Array.isArray(d.data.results)) results = d.data.results
}
// 统一将 id 转为字符串,避免类型不一致导致 Select 无法正确匹配显示
this.leaveApprovalOptions = (results || []).map(it => {
const rawId = it.user_id || it.id || it.userId || ''
return {
id: it.user_id || it.id || it.userId || '',
id: rawId === null || rawId === undefined ? '' : String(rawId),
name: it.user_name || it.name || it.userName || it.nick || ''
}
})
......@@ -415,7 +603,10 @@ export default {
},
openApplyModal () {
this.applyModal.isEdit = false
this.applyModal.form = { user_id: '', user_name: '', leave_type_id: '', start_time: '', end_time: '', duration_unit: 2, duration: 0, reason: '', emergency_contact: '', emergency_phone: '', approver_id: this.selectedApprovalUser || '' }
// 默认开始时间为当天(时分秒置为 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 || '' }
this.applyModal.visible = true
},
openEdit (row) {
......@@ -423,6 +614,12 @@ export default {
this.applyModal.form = Object.assign({}, row)
// 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 的类型一致(字符串)
if (this.applyModal.form.approver_id !== null && this.applyModal.form.approver_id !== undefined) {
this.applyModal.form.approver_id = String(this.applyModal.form.approver_id)
}
// 编辑时需要设置leave_id为主表id,如果没有则使用id作为回退
this.applyModal.form.leave_id = row.leave_id || row.id
this.applyModal.visible = true
},
saveApply () {
......@@ -471,36 +668,92 @@ export default {
} else this.$Notice.error({ title: '保存失败', desc: ret.data && ret.data.errmsg })
}).finally(() => { this.applyModal.saving = false })
},
canEdit (row) { return row.status === 0 || row.status === -1 },
canSubmit (row) { return row.status === 0 || row.status === -1 },
canRevoke (row) { return row.status === 1 },
canEdit (row) {
const status = row && row.status !== null && row.status !== undefined ? Number(row.status) : null
return status === 0
},
canSubmit (row) {
const status = row && row.status !== null && row.status !== undefined ? Number(row.status) : null
return status === 0
},
canRevoke (row) {
const status = row && row.status !== null && row.status !== undefined ? Number(row.status) : null
return status === 1
},
submit (row) {
this.$Modal.confirm({ title: '确认提交',
content: '确认提交此请假申请进入审批流程?',
onOk: () => {
submitLeaveApplication({ id: row.id }).then(ret => {
submitLeaveApplication({ 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 })
})
} })
},
revoke (row) {
revokeLeaveApplication({ id: row.id }).then(ret => {
revokeLeaveApplication({ 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 })
})
},
openApproveModal (row) {
this.approveModal.record = Object.assign({}, row)
this.approveModal.comment = ''
this.approveModal.transferEnabled = false
this.approveModal.transferApprover = ''
// 仅使用 selectTransferApprovalList 接口来获取审批流程与转审核选项(移除对 getLeaveById 的调用)
selectTransferApprovalList({ id: row.id }).then(ret => {
if (ret.data && ret.data.errcode === 0) {
const data = ret.data.data || []
// 填充转审核下拉选项
this.transferApprovalOptions = (data || []).map(it => {
const rawId = it.user_id || it.id || it.userId || ''
return {
id: rawId === null || rawId === undefined ? '' : String(rawId),
name: it.user_name || it.name || it.userName || it.nick || ''
}
})
// 直接使用该接口返回的数据作为审批流程展示(包含转审核记录)
this.approveModal.approvalFlow = (data || []).map(item => ({
// 仅显示后端返回的 approver_name(或 approverName),不再使用 transfer_to_name 回退
approver_name: item.approver_name || item.approverName || '',
transfer_reason: item.transfer_reason || '',
create_time: this.formatDisplayDatetime(item.create_time),
status: item.status,
node_index: item.node_index || '1'
}))
} else {
// 接口返回错误,清空数据并记录日志
this.transferApprovalOptions = []
this.approveModal.approvalFlow = []
console.error('获取转审核审批人列表失败', ret.data && ret.data.errmsg)
}
}).catch(err => {
// 出错时提供默认占位并清空展示
this.transferApprovalOptions = [{ id: '', name: '暂无审批人' }]
this.approveModal.approvalFlow = []
console.error('selectTransferApprovalList error', err)
}).finally(() => {
// 如果转审核已启用且未选择任何审批人,尝试选择第一个有效 id(非空字符串)
if (this.approveModal.transferEnabled) {
const firstValid = (this.transferApprovalOptions || []).find(opt => opt && opt.id !== null && opt.id !== undefined && String(opt.id).trim() !== '')
if (!this.approveModal.transferApprover) {
this.approveModal.transferApprover = firstValid ? firstValid.id : ''
}
}
this.approveModal.visible = true
})
},
canTransfer (row) {
// 仅当当前为审批中(status === 1)时允许转交;可根据需求调整条件
return row && (row.status === 1 || String(row.status) === '1')
// 仅当当前为审批中(status === 2)时允许转交;可根据需求调整条件
return row && (row.status === 2 || String(row.status) === '2')
},
openTransferModal (row) {
this.transferModal.record = Object.assign({}, row)
// 默认选择当前审批人(如果有)
this.transferModal.selectedApprover = row.approver_id || row.approverId || row.transfer_to_id || ''
if (this.transferModal.selectedApprover !== null && this.transferModal.selectedApprover !== undefined) {
this.transferModal.selectedApprover = String(this.transferModal.selectedApprover)
}
this.transferModal.comment = ''
this.transferModal.visible = true
},
......@@ -511,8 +764,10 @@ export default {
}
this.transferModal.processing = true
const approverOption = (this.leaveApprovalOptions || []).find(opt => opt.id === this.transferModal.selectedApprover)
console.log('this.transferModal.record', this.transferModal.record)
const payload = {
id: this.transferModal.record.id,
leave_id: this.transferModal.record.leave_id,
approver_id: this.transferModal.selectedApprover,
approver_name: approverOption ? approverOption.name : '',
comment: this.transferModal.comment || ''
......@@ -530,24 +785,85 @@ export default {
},
confirmApprove () {
this.approveModal.submitting = true
approveLeave({ id: this.approveModal.record.id, comment: this.approveModal.comment }).then(ret => {
approveLeave({ id: this.approveModal.record.id, leave_id: this.approveModal.record.leave_id || this.approveModal.record.id, comment: this.approveModal.comment }).then(ret => {
if (ret.data && ret.data.errcode === 0) { this.$Message.success('审批通过'); this.approveModal.visible = false; this.fetchList('pending') } else this.$Notice.error({ title: '审批失败', desc: ret.data && ret.data.errmsg })
}).finally(() => { this.approveModal.submitting = false })
},
confirmTransferApprove () {
if (!this.approveModal.transferApprover) {
this.$Message.warning('请选择要转交的审批人')
return
}
this.approveModal.submitting = true
const approverOption = (this.leaveApprovalOptions || []).find(opt => opt.id === this.approveModal.transferApprover)
const payload = {
id: this.approveModal.record.id,
leave_id: this.approveModal.record.leave_id || this.approveModal.record.id,
approver_id: this.approveModal.transferApprover,
approver_name: approverOption ? approverOption.name : '',
comment: this.approveModal.comment || ''
}
transferLeaveApproval(payload).then(ret => {
if (ret.data && ret.data.errcode === 0) {
this.$Message.success('转交成功')
this.approveModal.visible = false
this.fetchList('pending')
} else {
this.$Notice.error({ title: '转交失败', desc: ret.data && ret.data.errmsg })
}
}).finally(() => { this.approveModal.submitting = false })
},
confirmReject () {
if (!this.approveModal.comment || !this.approveModal.comment.trim()) { this.$Message.warning('请输入驳回原因'); return }
this.approveModal.submitting = true
rejectLeave({ id: this.approveModal.record.id, comment: this.approveModal.comment }).then(ret => {
rejectLeave({ id: this.approveModal.record.id, leave_id: this.approveModal.record.leave_id || this.approveModal.record.id, comment: this.approveModal.comment }).then(ret => {
if (ret.data && ret.data.errcode === 0) { this.$Message.success('已驳回'); this.approveModal.visible = false; this.fetchList('pending') } else this.$Notice.error({ title: '驳回失败', desc: ret.data && ret.data.errmsg })
}).finally(() => { this.approveModal.submitting = false })
},
refillApply () {
// 将批准记录数据反显到申请表单,清除 id 和 leave_id
this.applyModal.isEdit = false
this.applyModal.form = {
leave_type_id: this.approveModal.record.leave_type_id || '',
start_time: this.approveModal.record.start_time || '',
end_time: this.approveModal.record.end_time || '',
reason: this.approveModal.record.reason || '',
emergency_contact: this.approveModal.record.emergency_contact || '',
emergency_phone: this.approveModal.record.emergency_phone || '',
approver_id: this.approveModal.record.approver_id || this.selectedApprovalUser || ''
}
// 不保留 id 和 leave_id,保持为空
this.applyModal.visible = true
// 关闭审批弹窗
this.approveModal.visible = false
},
refillApplyDirect (row) {
// 直接从列表行数据进行重新填报,清除 id 和 leave_id
this.applyModal.isEdit = false
this.applyModal.form = {
leave_type_id: row.leave_type_id || '',
start_time: row.start_time || '',
end_time: row.end_time || '',
reason: row.reason || '',
emergency_contact: row.emergency_contact || '',
emergency_phone: row.emergency_phone || '',
approver_id: row.approver_id || this.selectedApprovalUser || ''
}
// 不保留 id 和 leave_id,保持为空
this.applyModal.visible = true
},
openDetail (row) {
this.detailModal.visible = true
this.detailModal.loading = true
getLeaveById({ id: row.id }).then(ret => {
getLeaveById({ id: row.leave_id || row.id }).then(ret => {
if (ret.data && ret.data.errcode === 0) {
const respData = ret.data.data || {}
this.detailModal.data = respData
// 格式化主记录时间字段(后端可能返回时间戳或字符串)
this.detailModal.data.start_time = this.formatDisplayDatetime(respData.start_time)
this.detailModal.data.end_time = this.formatDisplayDatetime(respData.end_time)
this.detailModal.data.submit_time = this.formatDisplayDatetime(respData.submit_time)
this.detailModal.data.approval_complete_time = this.formatDisplayDatetime(respData.approval_complete_time)
// 优先使用 subTables 中的 approvalRecords(如果存在),否则使用 respData.approvals
let approvals = []
if (respData.subTables && Array.isArray(respData.subTables.approvalRecords)) {
......@@ -561,7 +877,12 @@ export default {
return Object.assign({}, item, {
node_index: isNaN(nodeIndex) ? 0 : nodeIndex,
approver_time: this.formatDisplayDatetime(item ? item.approver_time : null),
approver_name: item ? (item.approver_name || item.approverName || '') : ''
// 详情中也只使用 approver_name / approverName,不再回退到 transfer_to_name
approver_name: item ? (item.approver_name || item.approverName || '') : '',
// 保留并统一转审备注字段,方便前端在详情表格中直接展示
transfer_reason: item ? (item.transfer_reason || item.transferReason || '') : '',
// 将 comment 设为 comment 或 transfer_reason(优先显示 comment)
comment: item ? (item.comment || '') : ''
})
})
approvals.sort((a, b) => (a.node_index || 0) - (b.node_index || 0))
......@@ -581,7 +902,7 @@ export default {
}
getLeaveStats({ params }).then(ret => {
if (ret.data && ret.data.errcode === 0) {
console.log("ret.data.data",ret.data.data)
console.log('ret.data.data', ret.data.data)
this.tables.stats = ret.data.data || []
} else {
this.$Notice.error({ title: '统计失败', desc: ret.data && ret.data.errmsg })
......@@ -607,4 +928,15 @@ export default {
.page_style { margin-top: 12px; text-align: right; }
.mt8 { margin-top: 8px; }
.action-buttons { display: flex; align-items: center; gap: 8px; justify-content: center; }
/* 审批流程样式:居中显示、固定高度、超出滚动 */
.approval-flow-section { max-height: 200px; overflow-y: auto; padding-right: 8px; margin-bottom: 8px; }
.flow-item { margin-bottom: 6px; padding: 8px 12px; background: transparent; border-radius: 0; border: none; text-align: left; }
.flow-row { align-items: center; display:flex; gap:0; }
.flow-row > [class*="ivu-col"] { padding-left: 0; padding-right: 0; }
.flow-col { display: inline-flex; align-items: center; gap: 4px; color: #6b6f76; font-size: 14px; line-height: 22px; }
.flow-col .label { color: #6b7785; font-weight: 400; min-width: 0; margin-right: 2px; padding-right: 0; }
.flow-col .value { display: inline-block; max-width: 100%; overflow: visible; text-overflow: unset; white-space: normal; color: #333; }
.flow-col .value.no-ellipsis { white-space: normal; overflow: visible; text-overflow: unset; }
.flow-reason { margin: 6px 0 0; color: #333; font-size: 13px; }
</style>
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论