Industrial-image-management.../src/views/hr/salary/system-insurance/health-management/index.vue

516 lines
14 KiB
Vue
Raw Normal View History

2025-07-30 09:13:52 +08:00
<template>
<GiPageLayout>
<div class="health-management-container">
<!-- 页面标题 -->
<div class="page-header">
<h2 class="page-title">健康档案管理</h2>
<a-button type="primary" @click="handleAddRecord">
<template #icon><icon-plus /></template>
2025-07-30 09:13:52 +08:00
添加记录
</a-button>
</div>
<!-- 搜索表单 -->
<a-card class="search-card" :bordered="false">
<a-form :model="searchForm" layout="inline">
<a-form-item>
<a-input
2025-07-30 09:13:52 +08:00
v-model="searchForm.keyword"
placeholder="搜索员工姓名或工号..."
style="width: 300px"
allow-clear
@press-enter="handleSearch"
/>
</a-form-item>
<a-form-item>
<a-button type="primary" @click="handleSearch">
<template #icon><icon-search /></template>
2025-07-30 09:13:52 +08:00
搜索
</a-button>
</a-form-item>
</a-form>
</a-card>
<!-- 健康档案表格 -->
<a-card class="table-card" :bordered="false">
<a-table
:columns="columns"
2025-07-30 09:13:52 +08:00
:data="tableData"
:pagination="pagination"
:loading="loading"
row-key="id"
@page-change="handlePageChange"
@page-size-change="handlePageSizeChange"
>
<template #result="{ record }">
<a-tag
2025-07-30 09:13:52 +08:00
:color="record.result === '正常' ? 'green' : record.result === '轻微异常' ? 'orange' : 'red'"
>
{{ record.result }}
</a-tag>
</template>
<template #action="{ record }">
<a-button
type="text"
size="small"
2025-07-30 09:13:52 +08:00
@click="handleView(record)"
>
查看
</a-button>
<a-button
type="text"
size="small"
2025-07-30 09:13:52 +08:00
@click="handleEdit(record)"
>
编辑
</a-button>
</template>
</a-table>
</a-card>
<!-- 添加/编辑记录弹窗 -->
<a-modal
v-model:visible="modalVisible"
:title="isEdit ? '编辑健康记录' : '添加健康记录'"
width="800px"
@ok="handleModalOk"
@cancel="handleModalCancel"
>
<a-form :model="formData" layout="vertical">
<a-row :gutter="20">
<a-col :span="24">
<a-form-item label="员工" required>
<a-select
v-model="formData.employeeId"
2025-07-30 09:13:52 +08:00
placeholder="请选择员工"
:loading="loadingEmployees"
allow-search
:filter-option="true"
>
<a-option
v-for="employee in employeeOptions"
:key="employee.id"
2025-07-30 09:13:52 +08:00
:value="employee.id"
>
{{ employee.nickname || employee.username }} ({{ employee.username }})
</a-option>
</a-select>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="20">
<a-col :span="24">
<a-form-item label="体检日期" required>
<a-date-picker v-model="formData.checkupDate" style="width: 100%" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="20">
<a-col :span="24">
<a-form-item label="体检医院" required>
<a-input v-model="formData.hospital" placeholder="请输入体检医院" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="20">
<a-col :span="24">
<a-form-item label="体检类型" required>
<a-select v-model="formData.checkType" placeholder="请选择体检类型">
<a-option value="常规体检">常规体检</a-option>
<a-option value="入职体检">入职体检</a-option>
<a-option value="年度体检">年度体检</a-option>
<a-option value="特殊体检">特殊体检</a-option>
</a-select>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="20">
<a-col :span="24">
<a-form-item label="体检结果" required>
<a-select v-model="formData.result" placeholder="请选择体检结果">
<a-option value="正常">正常</a-option>
<a-option value="轻微异常">轻微异常</a-option>
<a-option value="异常">异常</a-option>
</a-select>
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="20">
<a-col :span="24">
<a-form-item label="下次体检日期" required>
<a-date-picker v-model="formData.nextCheckupDate" style="width: 100%" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="20">
<a-col :span="24">
<a-form-item label="体检总结">
<a-textarea v-model="formData.summary" placeholder="请输入体检总结" :auto-size="{ minRows: 3, maxRows: 5 }" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="20">
<a-col :span="24">
<a-form-item label="医生建议">
<a-textarea v-model="formData.suggestions" placeholder="请输入医生建议" :auto-size="{ minRows: 3, maxRows: 5 }" />
</a-form-item>
</a-col>
</a-row>
<a-row :gutter="20">
<a-col :span="24">
<a-form-item label="体检报告">
<a-upload
:file-list="formData.reportFiles"
@change="handleFileChange"
2025-07-30 09:13:52 +08:00
action="#"
:before-upload="beforeUpload"
accept=".pdf,.doc,.docx,.jpg,.jpeg,.png"
>
<template #upload-button>
<a-button type="outline">选择文件</a-button>
</template>
</a-upload>
</a-form-item>
</a-col>
</a-row>
</a-form>
</a-modal>
<!-- 查看详情弹窗 -->
<a-modal
v-model:visible="detailModalVisible"
title="健康档案详情"
width="800px"
>
<div class="detail-content" v-if="selectedRecord">
2025-07-30 09:13:52 +08:00
<div class="detail-section">
<h4 class="section-title">基本信息</h4>
<div class="detail-grid">
<div class="detail-item">
<span class="detail-label">员工姓名</span>
<span class="detail-value">{{ selectedRecord.employeeName }}</span>
</div>
<div class="detail-item">
<span class="detail-label">工号</span>
<span class="detail-value">{{ selectedRecord.employeeId }}</span>
</div>
<div class="detail-item">
<span class="detail-label">体检日期</span>
<span class="detail-value">{{ selectedRecord.checkupDate }}</span>
</div>
<div class="detail-item">
<span class="detail-label">体检医院</span>
<span class="detail-value">{{ selectedRecord.hospital }}</span>
</div>
<div class="detail-item">
<span class="detail-label">体检结果</span>
<a-tag
2025-07-30 09:13:52 +08:00
:color="selectedRecord.result === '正常' ? 'green' : selectedRecord.result === '轻微异常' ? 'orange' : 'red'"
>
{{ selectedRecord.result }}
</a-tag>
</div>
<div class="detail-item">
<span class="detail-label">下次体检</span>
<span class="detail-value">{{ selectedRecord.nextCheckupDate }}</span>
</div>
</div>
</div>
<div class="detail-section" v-if="selectedRecord.remarks">
2025-07-30 09:13:52 +08:00
<h4 class="section-title">体检备注</h4>
<p class="remarks-text">{{ selectedRecord.remarks }}</p>
</div>
</div>
</a-modal>
</div>
</GiPageLayout>
</template>
<script setup lang="ts">
import { ref, reactive, onMounted } from 'vue'
2025-07-30 09:13:52 +08:00
import { Message } from '@arco-design/web-vue'
import {
IconPlus,
IconSearch
2025-07-30 09:13:52 +08:00
} from '@arco-design/web-vue/es/icon'
import { listAllUser } from '@/apis/system/user'
import type { UserResp } from '@/apis/system/type'
// 搜索表单数据
const searchForm = reactive({
keyword: ''
2025-07-30 09:13:52 +08:00
})
// 员工选项数据
const employeeOptions = ref<UserResp[]>([])
const loadingEmployees = ref(false)
// 表格列定义
const columns = [
{ title: '员工姓名', dataIndex: 'employeeName', width: 120 },
{ title: '工号', dataIndex: 'employeeId', width: 120 },
{ title: '最近体检日期', dataIndex: 'checkupDate', width: 140 },
{ title: '体检医院', dataIndex: 'hospital', width: 160 },
{ title: '体检结果', dataIndex: 'result', slotName: 'result', width: 120 },
{ title: '下次体检日期', dataIndex: 'nextCheckupDate', width: 140 },
{ title: '操作', slotName: 'action', width: 120 }
2025-07-30 09:13:52 +08:00
]
// 表格数据
const tableData = ref([
{
id: 1,
employeeName: '张三',
employeeId: 'EMP001',
checkupDate: '2023-10-15',
hospital: '北京协和医院',
result: '正常',
nextCheckupDate: '2024-10-15',
remarks: '身体状况良好,各项指标正常'
2025-07-30 09:13:52 +08:00
},
{
id: 2,
employeeName: '李四',
employeeId: 'EMP002',
checkupDate: '2023-09-20',
hospital: '上海瑞金医院',
result: '轻微异常',
nextCheckupDate: '2024-03-20',
remarks: '血压稍高,建议控制饮食,加强运动'
2025-07-30 09:13:52 +08:00
},
{
id: 3,
employeeName: '王五',
employeeId: 'EMP003',
checkupDate: '2023-08-10',
hospital: '广州中山医院',
result: '异常',
nextCheckupDate: '2023-11-10',
remarks: '肝功能异常,需要进一步检查和治疗'
}
2025-07-30 09:13:52 +08:00
])
// 分页配置
const pagination = reactive({
current: 1,
pageSize: 10,
total: 3,
showTotal: true,
showJumper: true,
showPageSize: true
2025-07-30 09:13:52 +08:00
})
// 加载状态
const loading = ref(false)
// 弹窗状态
const modalVisible = ref(false)
const detailModalVisible = ref(false)
const isEdit = ref(false)
// 表单数据
const formData = reactive({
id: null as number | null,
employeeName: '',
employeeId: '',
checkupDate: '',
hospital: '',
result: '',
nextCheckupDate: '',
remarks: '',
checkType: '',
summary: '',
suggestions: '',
reportFiles: [] as any[]
2025-07-30 09:13:52 +08:00
})
// 选中的记录
const selectedRecord = ref<any>(null)
// 搜索
const handleSearch = () => {
loading.value = true
setTimeout(() => {
loading.value = false
Message.success('搜索完成')
}, 1000)
}
// 分页改变
const handlePageChange = (current: number) => {
pagination.current = current
handleSearch()
}
const handlePageSizeChange = (pageSize: number) => {
pagination.pageSize = pageSize
handleSearch()
}
// 添加记录
const handleAddRecord = () => {
isEdit.value = false
modalVisible.value = true
Object.assign(formData, {
id: null,
employeeName: '',
employeeId: '',
checkupDate: '',
hospital: '',
result: '',
nextCheckupDate: '',
remarks: '',
checkType: '',
summary: '',
suggestions: '',
reportFiles: []
2025-07-30 09:13:52 +08:00
})
}
// 编辑记录
const handleEdit = (record: any) => {
isEdit.value = true
modalVisible.value = true
Object.assign(formData, record)
}
// 查看详情
const handleView = (record: any) => {
selectedRecord.value = record
detailModalVisible.value = true
}
// 弹窗确认
const handleModalOk = () => {
if (isEdit.value) {
Message.success('编辑健康记录成功')
} else {
Message.success('添加健康记录成功')
}
modalVisible.value = false
}
// 弹窗取消
const handleModalCancel = () => {
modalVisible.value = false
}
// 文件上传处理
const handleFileChange = (fileList: any[]) => {
formData.reportFiles = fileList
}
// 文件上传前检查
const beforeUpload = (file: any) => {
const isValidType = ['application/pdf', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'image/jpeg', 'image/jpg', 'image/png'].includes(file.type)
const isLt10M = file.size / 1024 / 1024 < 10
2025-07-30 09:13:52 +08:00
if (!isValidType) {
Message.error('文件类型错误,仅支持 PDF、Word、图片格式')
return false
}
if (!isLt10M) {
Message.error('文件大小不能超过10MB')
return false
}
return true
}
// 获取员工列表
const fetchEmployeeOptions = async () => {
try {
loadingEmployees.value = true
const response = await listAllUser({})
employeeOptions.value = response.data || []
} catch (error) {
console.error('获取员工列表失败:', error)
Message.error('获取员工列表失败')
} finally {
loadingEmployees.value = false
}
}
// 初始化
onMounted(() => {
handleSearch()
fetchEmployeeOptions()
})
</script>
<style scoped>
.health-management-container {
padding: 20px;
}
.page-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.page-title {
font-size: 24px;
font-weight: 600;
color: #1d2129;
margin: 0;
}
.search-card {
margin-bottom: 20px;
}
.table-card {
background: white;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.detail-content {
padding: 20px;
}
.detail-section {
margin-bottom: 24px;
}
.section-title {
font-size: 16px;
font-weight: 600;
color: #1d2129;
margin-bottom: 16px;
padding-bottom: 8px;
border-bottom: 1px solid #e5e6eb;
}
.detail-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 16px;
}
.detail-item {
display: flex;
align-items: center;
gap: 8px;
}
.detail-label {
font-weight: 600;
width: 120px;
flex-shrink: 0;
color: #1d2129;
}
.detail-value {
color: #4e5969;
word-break: break-word;
}
.remarks-text {
color: #4e5969;
line-height: 1.6;
margin: 0;
}
</style>