Files
klp-oa/klp-ui/src/views/wms/hrm/apply/goout.vue
砂糖 5b6286326b feat(HRM): 添加附件显示组件并优化表单逻辑
添加FileList组件用于显示附件列表
在请假和外出申请详情页中显示附件
优化审批部门选择逻辑,仅在新增时显示
修复请假申请编辑时的审批类型校验问题
2026-03-11 16:48:44 +08:00

400 lines
15 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="app-container">
<el-row :gutter="20">
<el-col :span="6">
<el-card>
<template slot="header">
<span>提交外出申请</span>
</template>
<!-- 左侧是新增表单 -->
<el-form ref="form" :model="form" :rules="rules" label-width="80px" v-loading="loading">
<el-form-item v-if="!form.outId" label="审批部门" prop="deptId">
<el-select v-model="form.deptId" placeholder="请选择审批部门" filterable @change="getDeptLeader">
<el-option v-for="item in deptOptions" :key="item.deptId"
:label="item.deptName + '(' + (item.leaderNickName || '无负责人') + ')'" :value="item.deptId"></el-option>
</el-select>
</el-form-item>
<el-form-item label="外出类型" prop="outType">
<el-select v-model="form.outType" placeholder="请选择外出类型">
<el-option v-for="dict in dict.type.hrm_out_type" :key="dict.value" :label="dict.label"
:value="dict.value"></el-option>
</el-select>
</el-form-item>
<el-form-item label="外出人姓名" prop="applicantName">
<employee-selector v-model="form.applicantName" :key-field="'name'" placeholder="请选择外出人姓名" />
</el-form-item>
<el-form-item label="开始时间" prop="startTime">
<el-date-picker clearable v-model="form.startTime" type="datetime" value-format="yyyy-MM-dd HH:mm:ss"
placeholder="请选择外出开始时间">
</el-date-picker>
</el-form-item>
<el-form-item label="结束时间" prop="endTime">
<el-date-picker clearable v-model="form.endTime" type="datetime" value-format="yyyy-MM-dd HH:mm:ss"
placeholder="请选择外出结束时间">
</el-date-picker>
</el-form-item>
<el-form-item label="外出小时数" prop="outHours">
<el-input v-model="form.outHours" placeholder="选择时间后自动计算,也可手动修改" />
</el-form-item>
<el-form-item label="外出地点" prop="outPlace">
<el-input v-model="form.outPlace" type="textarea" placeholder="请输入内容" />
</el-form-item>
<el-form-item label="外出原因" prop="outReason">
<el-input v-model="form.outReason" type="textarea" placeholder="请输入内容" />
</el-form-item>
<el-form-item label="附件" prop="attachmentUrls">
<FileUpload v-model="form.attachmentUrls" :max-count="1" :show-file-list="true" />
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" placeholder="请输入备注" />
</el-form-item>
</el-form>
<div style="text-align: center;">
<el-button type="primary" @click="handleSubmit" v-loading="loading">{{ form.outId ? '更新申请' : '提交申请'
}}</el-button>
<el-button @click="handleReset">重置表单</el-button>
</div>
</el-card>
</el-col>
<el-col :span="18">
<el-card>
<template slot="header">
<span>外出申请列表</span>
<el-button style="float: right;" icon="el-icon-refresh" @click="getList">刷新</el-button>
</template>
<el-table v-loading="loading" :data="leaveRequestList">
<el-table-column prop="approvalStatus" label="审批状态" align="center">
<template slot-scope="scope">
<el-tag :type="getStatusTagType(scope.row.approvalStatus)">
{{ getStatusText(scope.row.approvalStatus) }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="外出类型" align="center" prop="outType">
<template slot-scope="scope">
<dict-tag :options="dict.type.hrm_out_type" :value="scope.row.outType" />
</template>
</el-table-column>
<el-table-column label="外出人姓名" align="center" prop="applicantName" />
<el-table-column label="审批情况" align="center" prop="approverName">
<template slot-scope="scope">
<!-- 每行一个不要出现换行将英文映射成中文 -->
<el-tag v-for="task in scope.row.tasks" :key="task.taskId" :type="getTaskStatusTagType(task.taskStatus)" style="margin-right: 8px;">
<!-- taskStatus包括pending, approved, rejected, 根据状态设置不同的标签类型 -->
{{ task.approverName }} {{ getTaskStatusText(task.taskStatus) }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="开始时间" align="center" prop="startTime" width="120">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.startTime, '{y}-{m}-{d} {h}') }}</span>
</template>
</el-table-column>
<el-table-column label="结束时间" align="center" prop="endTime" width="120">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.endTime, '{y}-{m}-{d} {h}') }}</span>
</template>
</el-table-column>
<el-table-column label="外出小时数" align="center" prop="outHours" />
<el-table-column label="外出地点" align="center" prop="outPlace" show-overflow-tooltip />
<el-table-column label="外出原因" align="center" prop="outReason" show-overflow-tooltip />
<el-table-column label="操作" align="center" width="160">
<template slot-scope="scope">
<el-button icon="el-icon-printer" size="mini" v-if="scope.row.approvalStatus === '已同意'"
@click="handlePrint(scope.row)">打印</el-button>
<el-button icon="el-icon-edit" size="mini" @click="handleEdit(scope.row)"
v-if="scope.row.approvalStatus === '待审批'">修改</el-button>
<el-button icon="el-icon-delete" size="mini" @click="handleWithdraw(scope.row)"
v-if="scope.row.approvalStatus === '待审批'">撤回</el-button>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize" @pagination="getList" />
</el-card>
</el-col>
</el-row>
<out-label-printer ref="outLabelPrinter" :printer-info="printerInfo"></out-label-printer>
</div>
</template>
<script>
import { getOutRequest, addOutRequest, updateOutRequest } from "@/api/wms/outRequest";
import { listApproval, updateApproval, withdrawApproval } from "@/api/wms/approval"
import { listDept } from "@/api/wms/dept"
import FileUpload from '@/components/FileUpload'
import EmployeeSelector from '@/components/EmployeeSelector'
import OutLabelPrinter from '../components/outLabelPrinter'
export default {
name: 'LeaveApply',
dicts: ['hrm_leave_shift', 'hrm_out_type', 'hrm_department'],
components: {
FileUpload,
EmployeeSelector,
OutLabelPrinter
},
data() {
return {
// 表单参数
form: {},
loading: false,
buttonLoading: false,
leaveRequestList: [],
total: 0,
queryParams: {
pageNum: 1,
pageSize: 10,
leaveStatus: undefined,
applyType: 'out',
createBy: this.$store.getters.name,
},
// 表单校验规则【核心新增:完整必填校验】
rules: {
outType: [{ required: true, message: '外出类型不能为空', trigger: 'change' }],
applicantName: [{ required: true, message: '外出人姓名不能为空', trigger: 'change' }],
startTime: [{ required: true, message: '开始时间不能为空', trigger: 'change' }],
endTime: [{ required: true, message: '结束时间不能为空', trigger: 'change' }],
outHours: [{ required: true, message: '外出小时数不能为空', trigger: ['blur', 'change'] }],
outPlace: [{ required: true, message: '外出地点不能为空', trigger: ['blur', 'change'] }],
outReason: [{ required: true, message: '外出原因不能为空', trigger: ['blur', 'change'] }],
deptId: [{ required: true, message: '审批部门不能为空', trigger: 'change' }],
},
deptOptions: [],
printerInfo: {},
}
},
// 核心新增:监听开始/结束时间变化,自动计算天数
watch: {
'form.startTime': {
handler() {
this.calculateLeaveDays()
},
immediate: false
},
'form.endTime': {
handler() {
this.calculateLeaveDays()
},
immediate: false
},
},
created() {
this.getList();
this.getDeptList();
},
methods: {
getDeptList() {
listDept().then(response => {
this.deptOptions = response.data
})
},
// 获取部门负责人
getDeptLeader() {
const selectedDept = this.deptOptions.find(item => item.deptId === this.form.deptId)
const approverName = selectedDept.leaderNickName;
if (!approverName) {
this.$message.warning('该部门无负责人,申请将无人审批');
return;
}
this.form.approverName = approverName
},
/** 查询外出申请列表 */
getList() {
this.loading = true;
listApproval(this.queryParams).then(response => {
this.leaveRequestList = response.rows.map(item => {
return {
approvalStatus: item.approval.approvalStatus,
applyId: item.approval.applyId,
approvalId: item.approval.approvalId,
approvalType: item.approval.approvalType,
approverName: item.approval.approverName,
tasks: item.tasks,
...item.detail,
}
});
this.total = response.total;
this.loading = false;
});
},
handleReset() {
this.reset()
},
// 表单重置
reset() {
this.form = {
outId: undefined,
outTitle: undefined,
outType: undefined,
applicantName: undefined,
deptId: undefined,
startTime: undefined,
endTime: undefined,
outHours: undefined,
outPlace: undefined,
outReason: undefined,
attachmentUrls: undefined,
createBy: undefined,
createTime: undefined,
updateBy: undefined,
updateTime: undefined,
delFlag: undefined,
remark: undefined,
approvalType: 'single'
};
this.resetForm("form");
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
/** 修改按钮操作 */
handleEdit(row) {
this.loading = true;
this.reset();
const outId = row.applyId
getOutRequest(outId).then(response => {
this.loading = false;
this.form = response.data;
});
},
handlePrint(row) {
const startYear = new Date(row.startTime).getFullYear()
const endYear = new Date(row.endTime).getFullYear()
const startMonth = new Date(row.startTime).getMonth() + 1
const endMonth = new Date(row.endTime).getMonth() + 1
const startDay = new Date(row.startTime).getDate()
const endDay = new Date(row.endTime).getDate()
const applyName = row.applicantName
const approverName = row.approverName
const reason = row.outReason
const outHours = parseInt(row.outHours)
const outPlace = row.outPlace
this.printerInfo = {
startYear,
endYear,
startMonth,
endMonth,
startDay,
endDay,
applyName,
approverName,
reason,
outHours,
outPlace,
}
this.$nextTick(() => {
this.$refs["outLabelPrinter"].print()
})
},
/** 提交按钮 */
handleSubmit() {
this.form.outTitle = `${this.form.applicantName}-${this.form.outType}-${this.form.startTime}-${this.form.outReason || ''}`
this.$refs["form"].validate(valid => {
if (valid) {
this.buttonLoading = true;
if (this.form.outId != null) {
updateOutRequest(this.form).then(response => {
this.$modal.msgSuccess("修改成功");
this.getList();
this.reset()
}).finally(() => {
this.buttonLoading = false;
});
} else {
addOutRequest(this.form).then(response => {
this.$modal.msgSuccess("新增成功");
this.getList();
this.reset()
}).finally(() => {
this.buttonLoading = false;
});
}
}
});
},
handleWithdraw(row) {
this.$modal.confirm('是否确认撤回外出申请编号为"' + row.applyId + '"的数据项?').then(() => {
this.loading = true;
// 撤销审批
return withdrawApproval(row.approvalId)
}).then(() => {
this.loading = false;
this.getList();
this.$modal.msgSuccess("撤回成功");
}).catch(() => {
}).finally(() => {
this.loading = false;
});
},
// 获取审批状态对应的标签类型
getStatusTagType(status) {
const typeMap = {
'待审批': 'warning',
'已同意': 'success',
'已驳回': 'danger',
'已撤销': 'info',
}
return typeMap[status] || 'default'
},
// 获取审批状态的中文文本
getStatusText(status) {
const textMap = {
'待审批': '待审批',
'已同意': '已同意',
'已驳回': '已驳回',
'已撤销': '已撤销',
}
return textMap[status] || '未知状态'
},
// 获取任务状态的中文文本
getTaskStatusText(status) {
const textMap = {
'pending': '待审批',
'approved': '已同意',
'rejected': '已驳回'
}
return textMap[status] || status
},
// 获取任务状态的标签类型
getTaskStatusTagType(status) {
const typeMap = {
'pending': 'warning',
'approved': 'success',
'rejected': 'danger'
}
return typeMap[status] || 'info'
},
// 核心新增:自动计算外出小时数的方法
calculateLeaveDays() {
const { startTime, endTime } = this.form;
// 两个时间都选择后才计算
if (startTime && endTime) {
// 转成时间戳
const start = new Date(startTime).getTime();
const end = new Date(endTime).getTime();
// 判断结束时间不能小于开始时间
if (end < start) {
this.$modal.msgWarning('结束时间不能早于开始时间,请重新选择!');
this.form.leaveDays = undefined;
return;
}
// 计算时间差(毫秒) → 转天 → 保留2位小数
const diffTime = end - start;
const diffHours = parseInt((diffTime / (1000 * 60 * 60)));
// 赋值到天数输入框
this.form.outHours = diffHours;
}
}
}
}
</script>