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

516 lines
14 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>
<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>
添加记录
</a-button>
</div>
<!-- 搜索表单 -->
<a-card class="search-card" :bordered="false">
<a-form :model="searchForm" layout="inline">
<a-form-item>
<a-input
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>
搜索
</a-button>
</a-form-item>
</a-form>
</a-card>
<!-- 健康档案表格 -->
<a-card class="table-card" :bordered="false">
<a-table
:columns="columns"
:data="tableData"
:pagination="pagination"
:loading="loading"
row-key="id"
@page-change="handlePageChange"
@page-size-change="handlePageSizeChange"
>
<template #result="{ record }">
<a-tag
:color="record.result === '正常' ? 'green' : record.result === '轻微异常' ? 'orange' : 'red'"
>
{{ record.result }}
</a-tag>
</template>
<template #action="{ record }">
<a-button
type="text"
size="small"
@click="handleView(record)"
>
查看
</a-button>
<a-button
type="text"
size="small"
@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"
placeholder="请选择员工"
:loading="loadingEmployees"
allow-search
:filter-option="true"
>
<a-option
v-for="employee in employeeOptions"
:key="employee.id"
: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"
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">
<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
: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">
<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'
import { Message } from '@arco-design/web-vue'
import {
IconPlus,
IconSearch
} from '@arco-design/web-vue/es/icon'
import { listAllUser } from '@/apis/system/user'
import type { UserResp } from '@/apis/system/type'
// 搜索表单数据
const searchForm = reactive({
keyword: ''
})
// 员工选项数据
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 }
]
// 表格数据
const tableData = ref([
{
id: 1,
employeeName: '张三',
employeeId: 'EMP001',
checkupDate: '2023-10-15',
hospital: '北京协和医院',
result: '正常',
nextCheckupDate: '2024-10-15',
remarks: '身体状况良好,各项指标正常'
},
{
id: 2,
employeeName: '李四',
employeeId: 'EMP002',
checkupDate: '2023-09-20',
hospital: '上海瑞金医院',
result: '轻微异常',
nextCheckupDate: '2024-03-20',
remarks: '血压稍高,建议控制饮食,加强运动'
},
{
id: 3,
employeeName: '王五',
employeeId: 'EMP003',
checkupDate: '2023-08-10',
hospital: '广州中山医院',
result: '异常',
nextCheckupDate: '2023-11-10',
remarks: '肝功能异常,需要进一步检查和治疗'
}
])
// 分页配置
const pagination = reactive({
current: 1,
pageSize: 10,
total: 3,
showTotal: true,
showJumper: true,
showPageSize: true
})
// 加载状态
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[]
})
// 选中的记录
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: []
})
}
// 编辑记录
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
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>