516 lines
14 KiB
Vue
516 lines
14 KiB
Vue
<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> |