Industrial-image-management.../src/views/hr/salary/certification/index.vue

547 lines
15 KiB
Vue
Raw Normal View History

2025-07-14 11:11:33 +08:00
<template>
<GiPageLayout>
<div class="certification-manage-container">
<div class="page-header">
<h2 class="page-title">人员资质管理</h2>
<a-button type="primary" @click="showAddModal">
<template #icon>
<icon-plus />
</template>
新增资质
</a-button>
</div>
<!-- 搜索表单 -->
<a-card class="search-card" :bordered="false">
<a-form :model="searchForm" layout="inline">
<a-form-item label="证书名称" field="certificationName">
2025-07-24 20:53:50 +08:00
<a-input v-model="searchForm.certificationName" placeholder="请输入证书名称" allow-clear style="width: 200px" />
2025-07-14 11:11:33 +08:00
</a-form-item>
<a-form-item label="证书类型" field="certificationType">
2025-07-24 20:53:50 +08:00
<a-input v-model="searchForm.certificationType" placeholder="请输入证书类型" allow-clear style="width: 200px" />
2025-07-14 11:11:33 +08:00
</a-form-item>
<a-form-item label="用户姓名" field="userName">
2025-07-24 20:53:50 +08:00
<a-input v-model="searchForm.userName" placeholder="请输入用户姓名" allow-clear style="width: 200px" />
2025-07-14 11:11:33 +08:00
</a-form-item>
<a-form-item>
<a-space>
<a-button type="primary" @click="handleSearch">
<template #icon>
<icon-search />
</template>
搜索
</a-button>
<a-button @click="handleReset">
<template #icon>
<icon-refresh />
</template>
重置
</a-button>
</a-space>
</a-form-item>
</a-form>
</a-card>
<!-- 人员资质表格 -->
<a-card class="table-card" :bordered="false">
2025-07-24 20:53:50 +08:00
<a-table :columns="columns" :data="certificationList" :pagination="paginationConfig" :loading="loading"
row-key="certificationId" @page-change="handlePageChange">
2025-07-14 11:11:33 +08:00
<template #userName="{ record }">
<span>{{ getUserName(record.userId) }}</span>
</template>
<template #certificationImage="{ record }">
2025-07-24 20:53:50 +08:00
<a-image v-if="record.certificationImage" :src="record.certificationImage" width="60" height="40"
fit="cover" show-loader preview />
2025-07-14 11:11:33 +08:00
<span v-else>-</span>
</template>
<template #validityPeriod="{ record }">
<span>{{ record.validityDateBegin }} {{ record.validityDateEnd }}</span>
</template>
<template #actions="{ record }">
<a-space>
<a-button type="primary" size="small" @click="editRecord(record)">
编辑
</a-button>
<a-button type="primary" status="danger" size="small" @click="deleteRecord(record)">
删除
</a-button>
</a-space>
</template>
</a-table>
</a-card>
<!-- 新增/编辑资质信息模态框 -->
2025-07-24 20:53:50 +08:00
<a-modal v-model:visible="modalVisible" :title="isEdit ? '编辑资质信息' : '新增资质信息'" width="700px" @ok="handleSubmit"
@cancel="handleCancel">
<a-form ref="formRef" :model="formData" :rules="formRules" layout="vertical">
2025-07-14 11:11:33 +08:00
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="证书编号" field="certificationCode">
<a-input v-model="formData.certificationCode" placeholder="请输入证书编号" />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="证书名称" field="certificationName">
<a-input v-model="formData.certificationName" placeholder="请输入证书名称" />
</a-form-item>
</a-col>
</a-row>
<a-form-item label="证书类型" field="certificationType">
<a-select v-model="formData.certificationType" placeholder="请选择证书类型">
<a-option v-for="item in CERTIFICATION_TYPE_OPTIONS" :key="item.value" :value="item.value"
:label="item.label">
{{ item.label }}
</a-option>
</a-select>
</a-form-item>
2025-07-14 11:11:33 +08:00
<a-col :span="12">
<a-form-item label="持证人" field="userId">
<a-select v-model="formData.userId" placeholder="请选择持证人" :loading="loadingUsers" allow-search
:filter-option="filterUserOption" @="console.log('d', formData)">
<a-option v-for="user in userList" :key="user.userId" :value="user.userId" :label="user.name">
{{ user.name }}({{ user.account }})
</a-option>
</a-select>
</a-form-item>
</a-col>
2025-07-14 11:11:33 +08:00
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="有效期开始" field="validityDateBegin">
2025-07-24 20:53:50 +08:00
<a-date-picker v-model="formData.validityDateBegin" placeholder="请选择有效期开始日期" style="width: 100%" />
2025-07-14 11:11:33 +08:00
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="有效期结束" field="validityDateEnd">
2025-07-24 20:53:50 +08:00
<a-date-picker v-model="formData.validityDateEnd" placeholder="请选择有效期结束日期" style="width: 100%" />
2025-07-14 11:11:33 +08:00
</a-form-item>
</a-col>
</a-row>
<a-form-item label="证书图片" field="certificationImage">
2025-07-24 20:53:50 +08:00
<a-upload :custom-request="handleUpload" :show-file-list="false" accept="image/*"
:before-upload="beforeUpload">
2025-07-14 11:11:33 +08:00
<template #upload-button>
<div class="upload-wrapper">
2025-07-24 20:53:50 +08:00
<a-image v-if="formData.certificationImage" :src="formData.certificationImage" width="200"
height="120" fit="cover" show-loader preview />
2025-07-14 11:11:33 +08:00
<div v-else class="upload-placeholder">
<icon-plus />
<div>点击上传证书图片</div>
</div>
</div>
</template>
</a-upload>
</a-form-item>
</a-form>
</a-modal>
</div>
</GiPageLayout>
</template>
<script setup lang="ts">
import { ref, reactive, onMounted, computed } from 'vue'
import { Message, Modal } from '@arco-design/web-vue'
import { IconPlus, IconSearch, IconRefresh } from '@arco-design/web-vue/es/icon'
import * as CertificationAPI from '@/apis/employee'
import type { CertificationInfo, CertificationListParams, SimpleUserInfo } from '@/apis/employee'
import { uploadFile } from '@/apis/common/common'
defineOptions({ name: 'HRCertification' })
// 数据状态
const loading = ref(false)
const loadingUsers = ref(false)
const modalVisible = ref(false)
const isEdit = ref(false)
const formRef = ref()
// 列表数据
2025-07-24 20:53:50 +08:00
const certificationList = ref<CertificationAPI.CertificationListResponse>()
2025-07-14 11:11:33 +08:00
const userList = ref<SimpleUserInfo[]>([])
// 搜索表单
const searchForm = reactive<CertificationListParams>({
certificationName: '',
certificationType: '',
userName: ''
})
// 分页配置
const paginationConfig = reactive({
current: 1,
pageSize: 10,
total: 0,
showTotal: true,
showJumper: true
})
// 表单数据
const formData = reactive<CertificationInfo>({
certificationCode: '',
2025-07-24 20:53:50 +08:00
certificationImage: 'n',
2025-07-14 11:11:33 +08:00
certificationName: '',
certificationType: '',
userId: '',
validityDateBegin: '',
2025-07-24 20:53:50 +08:00
validityDateEnd: '',
// certificationTypeLabel: ''
2025-07-14 11:11:33 +08:00
})
// 表单验证规则
const formRules = {
certificationCode: [
{ required: true, message: '请输入证书编号' }
],
certificationName: [
{ required: true, message: '请输入证书名称' }
],
certificationType: [
{ required: true, message: '请输入证书类型' }
],
userId: [
{ required: true, message: '请选择持证人' }
],
validityDateBegin: [
{ required: true, message: '请选择有效期开始日期' }
],
validityDateEnd: [
{ required: true, message: '请选择有效期结束日期' }
]
}
const CERTIFICATION_TYPE_OPTIONS = [
{ label: '高处作业', value: 'height-operation' },
{ label: '低压电工', value: 'low-voltage-operation' },
{ label: '高压电工', value: 'high-voltage-operation' },
{ label: '海上交通安全', value: 'maritime-traffic-safety' },
{ label: '驾驶证', value: 'driving-license' },
{ label: '防雷检测', value: 'lightning-protection-detection' },
{ label: '无人机驾驶', value: 'drone-driving' }
]
2025-07-14 11:11:33 +08:00
// 表格列定义
const columns = [
{
title: '证书编号',
dataIndex: 'certificationCode',
width: 150
},
{
title: '证书名称',
dataIndex: 'certificationName',
width: 200
},
{
title: '证书类型',
dataIndex: 'certificationType',
width: 150,
render: ({ record }) => {
const type = CERTIFICATION_TYPE_OPTIONS.find(item => item.value === record.certificationType)
return type ? type.label : '-'
}
2025-07-14 11:11:33 +08:00
},
{
title: '持证人',
2025-07-24 20:53:50 +08:00
dataIndex: 'userName',
2025-07-14 11:11:33 +08:00
slotName: 'userName',
width: 120
},
{
title: '证书图片',
dataIndex: 'certificationImage',
slotName: 'certificationImage',
width: 100
},
{
title: '有效期',
dataIndex: 'validityPeriod',
slotName: 'validityPeriod',
width: 200
},
{
title: '操作',
slotName: 'actions',
width: 150,
fixed: 'right'
}
]
// 获取用户名称
const getUserName = (userId: string) => {
const user = userList.value.find(u => u.userId === userId)
return user ? user.name : '-'
}
// 用户搜索过滤
const filterUserOption = (inputValue: string, option: any) => {
const user = userList.value.find(u => u.userId === option.value)
if (!user) return false
2025-07-24 20:53:50 +08:00
2025-07-14 11:11:33 +08:00
const searchText = inputValue.toLowerCase()
2025-07-24 20:53:50 +08:00
return user.name.toLowerCase().includes(searchText) ||
user.account.toLowerCase().includes(searchText)
2025-07-14 11:11:33 +08:00
}
// 获取用户列表
const getUserList = async () => {
try {
loadingUsers.value = true
const response = await CertificationAPI.getUserList()
2025-07-24 20:53:50 +08:00
2025-07-14 11:11:33 +08:00
console.log('用户列表响应:', response)
2025-07-24 20:53:50 +08:00
2025-07-14 11:11:33 +08:00
if (response.data) {
2025-07-24 20:53:50 +08:00
userList.value = response.data.map(item => ({
userId: item.userId,
name: item.name,
account: item.account,
})) || []
console.log('用户列表响应2:', userList.value)
2025-07-14 11:11:33 +08:00
}
} catch (error) {
console.error('获取用户列表失败:', error)
Message.error('获取用户列表失败')
} finally {
loadingUsers.value = false
}
}
// 获取资质信息列表
const getCertificationList = async () => {
try {
loading.value = true
const params: CertificationListParams = {
...searchForm,
}
const response = await CertificationAPI.getCertificationList(params)
if (response.data) {
2025-07-24 20:53:50 +08:00
certificationList.value = response.data || []
2025-07-14 11:11:33 +08:00
paginationConfig.total = response.data.total || 0
2025-07-24 20:53:50 +08:00
console.log('cc', certificationList.value)
console.log('cd', response.data[0])
2025-07-14 11:11:33 +08:00
}
} catch (error) {
Message.error('获取资质信息列表失败')
} finally {
loading.value = false
}
}
// 文件上传前检查
const beforeUpload = (file: File) => {
const isImage = file.type.startsWith('image/')
if (!isImage) {
Message.error('只能上传图片文件')
return false
}
2025-07-24 20:53:50 +08:00
2025-07-14 11:11:33 +08:00
const isLt2M = file.size / 1024 / 1024 < 2
if (!isLt2M) {
Message.error('图片大小不能超过 2MB')
return false
}
2025-07-24 20:53:50 +08:00
2025-07-14 11:11:33 +08:00
return true
}
// 文件上传处理
const handleUpload = async (option: any) => {
try {
const uploadFormData = new FormData()
uploadFormData.append('file', option.fileItem.file)
2025-07-24 20:53:50 +08:00
2025-07-14 11:11:33 +08:00
const response = await uploadFile(uploadFormData)
2025-07-24 20:53:50 +08:00
2025-07-14 11:11:33 +08:00
if (response.data && response.data.url) {
formData.certificationImage = response.data.url
Message.success('图片上传成功')
option.onSuccess()
} else {
Message.error('图片上传失败')
option.onError()
}
} catch (error) {
console.error('图片上传失败:', error)
Message.error('图片上传失败')
option.onError()
}
}
// 搜索
const handleSearch = () => {
paginationConfig.current = 1
getCertificationList()
}
// 重置搜索
const handleReset = () => {
Object.assign(searchForm, {
certificationName: '',
certificationType: '',
userName: ''
})
paginationConfig.current = 1
getCertificationList()
}
// 分页变化
const handlePageChange = (page: number) => {
paginationConfig.current = page
getCertificationList()
}
// 显示新增模态框
const showAddModal = () => {
isEdit.value = false
resetForm()
modalVisible.value = true
}
// 编辑记录
const editRecord = (record: CertificationInfo) => {
isEdit.value = true
Object.assign(formData, record)
modalVisible.value = true
}
// 删除记录
const deleteRecord = (record: CertificationInfo) => {
Modal.confirm({
title: '确认删除',
content: '确定要删除这条资质信息吗?',
okText: '确认',
cancelText: '取消',
onOk: async () => {
try {
await CertificationAPI.deleteCertification(record.certificationId!)
Message.success('删除成功')
await getCertificationList()
} catch (error) {
console.error('删除失败:', error)
Message.error('删除失败')
}
}
})
}
// 表单提交
const handleSubmit = async () => {
try {
await formRef.value?.validate()
2025-07-24 20:53:50 +08:00
2025-07-14 11:11:33 +08:00
if (isEdit.value) {
await CertificationAPI.updateCertification(formData.certificationId!, formData)
Message.success('更新成功')
} else {
await CertificationAPI.createCertification(formData)
Message.success('创建成功')
}
2025-07-24 20:53:50 +08:00
2025-07-14 11:11:33 +08:00
modalVisible.value = false
resetForm()
await getCertificationList()
} catch (error) {
console.error('操作失败:', error)
Message.error('操作失败,请重试')
}
}
// 取消操作
const handleCancel = () => {
modalVisible.value = false
resetForm()
}
// 重置表单
const resetForm = () => {
Object.assign(formData, {
certificationId: '',
certificationCode: '',
certificationImage: '',
certificationName: '',
certificationType: '',
userId: '',
validityDateBegin: '',
validityDateEnd: ''
})
formRef.value?.resetFields()
}
// 初始化
const init = async () => {
await Promise.all([
getUserList(),
getCertificationList()
])
}
// 组件挂载
onMounted(() => {
init()
})
</script>
<style scoped>
.certification-manage-container {
padding: 20px;
}
.page-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.page-title {
margin: 0;
font-size: 18px;
font-weight: 500;
}
.search-card {
margin-bottom: 20px;
}
.table-card {
margin-bottom: 20px;
}
.upload-wrapper {
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
transition: border-color 0.3s;
}
.upload-wrapper:hover {
border-color: #40a9ff;
}
.upload-placeholder {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 200px;
height: 120px;
background-color: #fafafa;
color: #999;
}
.upload-placeholder .arco-icon {
font-size: 24px;
margin-bottom: 8px;
}
2025-07-24 20:53:50 +08:00
</style>