<template>
|
<div class="signed-patient-page">
|
<!-- 搜索筛选区域 -->
|
<el-card class="search-card" shadow="never">
|
<el-form :model="queryParams" ref="queryForm" :inline="true" label-width="100px">
|
<el-row :gutter="20">
|
<el-col :span="6">
|
<el-form-item label="患者姓名" prop="patientName">
|
<el-input
|
v-model="queryParams.patientName"
|
placeholder="请输入患者姓名"
|
clearable
|
@keyup.enter="handleQuery"
|
/>
|
</el-form-item>
|
</el-col>
|
<el-col :span="6">
|
<el-form-item label="签约状态" prop="contractStatus">
|
<el-select v-model="queryParams.contractStatus" placeholder="请选择状态" clearable>
|
<el-option label="正常服务中" value="1" />
|
<el-option label="即将到期" value="2" />
|
<el-option label="已到期" value="3" />
|
<el-option label="已终止" value="4" />
|
</el-select>
|
</el-form-item>
|
</el-col>
|
<el-col :span="6">
|
<el-form-item label="服务套餐" prop="servicePackage">
|
<el-select v-model="queryParams.servicePackage" placeholder="请选择套餐" clearable>
|
<el-option label="基础健康管理包" value="1" />
|
<el-option label="慢性病管理包" value="2" />
|
<el-option label="老年人健康包" value="3" />
|
<el-option label="孕产妇保健包" value="4" />
|
<el-option label="儿童保健包" value="5" />
|
</el-select>
|
</el-form-item>
|
</el-col>
|
<el-col :span="6">
|
<el-form-item>
|
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
|
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
|
</el-form-item>
|
</el-col>
|
</el-row>
|
</el-form>
|
</el-card>
|
|
<!-- 统计信息卡片 -->
|
<el-row :gutter="20" class="stats-row">
|
<el-col :span="6">
|
<el-card class="stats-card" shadow="hover">
|
<div class="stats-content">
|
<div class="stats-number">{{ stats.total }}</div>
|
<div class="stats-label">总签约患者</div>
|
</div>
|
</el-card>
|
</el-col>
|
<el-col :span="6">
|
<el-card class="stats-card" shadow="hover">
|
<div class="stats-content">
|
<div class="stats-number" style="color: #67C23A;">{{ stats.active }}</div>
|
<div class="stats-label">正常服务中</div>
|
</div>
|
</el-card>
|
</el-col>
|
<el-col :span="6">
|
<el-card class="stats-card" shadow="hover">
|
<div class="stats-content">
|
<div class="stats-number" style="color: #E6A23C;">{{ stats.expiring }}</div>
|
<div class="stats-label">即将到期</div>
|
</div>
|
</el-card>
|
</el-col>
|
<el-col :span="6">
|
<el-card class="stats-card" shadow="hover">
|
<div class="stats-content">
|
<div class="stats-number" style="color: #F56C6C;">{{ stats.expired }}</div>
|
<div class="stats-label">已到期/终止</div>
|
</div>
|
</el-card>
|
</el-col>
|
</el-row>
|
|
<!-- 数据表格 -->
|
<el-card shadow="never">
|
<el-table
|
v-loading="loading"
|
:data="patientList"
|
@selection-change="handleSelectionChange"
|
style="width: 100%"
|
:default-sort="{prop: 'signDate', order: 'descending'}"
|
>
|
<el-table-column type="selection" width="55" />
|
<el-table-column label="患者信息" min-width="180" fixed>
|
<template slot-scope="scope">
|
<div class="patient-info">
|
<div class="patient-name">{{ scope.row.patientName }}</div>
|
<div class="patient-detail">
|
{{ scope.row.gender }} | {{ scope.row.age }}岁 | {{ scope.row.phone }}
|
</div>
|
</div>
|
</template>
|
</el-table-column>
|
<el-table-column label="身份证号" prop="idCard" width="180" />
|
<el-table-column label="签约医生" prop="doctorName" width="120" />
|
<el-table-column label="服务套餐" min-width="150">
|
<template slot-scope="scope">
|
<el-tag :type="getPackageType(scope.row.servicePackageId)">
|
{{ scope.row.servicePackage }}
|
</el-tag>
|
<div style="font-size: 12px; color: #666; margin-top: 4px;">
|
周期: {{ scope.row.contractPeriod }}年
|
</div>
|
</template>
|
</el-table-column>
|
<el-table-column label="包含服务" min-width="200">
|
<template slot-scope="scope">
|
<el-tooltip effect="dark" :content="scope.row.services.join('、')" placement="top">
|
<span>{{ scope.row.services.slice(0, 2).join('、') }}等{{ scope.row.services.length }}项</span>
|
</el-tooltip>
|
</template>
|
</el-table-column>
|
<el-table-column label="签约时间" prop="signDate" width="120" sortable />
|
<el-table-column label="到期时间" prop="expireDate" width="120" />
|
<el-table-column label="剩余天数" width="100">
|
<template slot-scope="scope">
|
<span :class="getRemainingDaysClass(scope.row.remainingDays)">
|
{{ scope.row.remainingDays }}天
|
</span>
|
</template>
|
</el-table-column>
|
<el-table-column label="签约状态" width="100" fixed="right">
|
<template slot-scope="scope">
|
<el-tag :type="getStatusType(scope.row.contractStatus)" effect="dark">
|
{{ getStatusText(scope.row.contractStatus) }}
|
</el-tag>
|
</template>
|
</el-table-column>
|
<el-table-column label="操作" width="200" fixed="right">
|
<template slot-scope="scope">
|
<el-button size="mini" type="text" @click="handleView(scope.row)">详情</el-button>
|
<el-button
|
size="mini"
|
type="text"
|
@click="handleRenew(scope.row)"
|
:disabled="scope.row.contractStatus === 1"
|
>续约</el-button>
|
<el-button
|
size="mini"
|
type="text"
|
@click="handleTerminate(scope.row)"
|
:disabled="scope.row.contractStatus === 4"
|
style="color: #F56C6C;"
|
>终止</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>
|
</div>
|
</template>
|
|
<script>
|
export default {
|
name: 'SignedPatientList',
|
data() {
|
return {
|
loading: false,
|
ids: [],
|
stats: {
|
total: 0,
|
active: 0,
|
expiring: 0,
|
expired: 0
|
},
|
queryParams: {
|
pageNum: 1,
|
pageSize: 10,
|
patientName: undefined,
|
contractStatus: undefined,
|
servicePackage: undefined
|
},
|
total: 0,
|
patientList: [],
|
// 服务套餐配置
|
servicePackages: {
|
'1': {
|
name: '基础健康管理包',
|
services: ['年度健康评估', '在线健康咨询', '健康档案管理', '定期健康提醒'],
|
price: 0,
|
color: 'info'
|
},
|
'2': {
|
name: '慢性病管理包',
|
services: ['专属医生服务', '用药指导管理', '定期随访监测', '个性化康复计划', '紧急医疗咨询'],
|
price: 299,
|
color: 'success'
|
},
|
'3': {
|
name: '老年人健康包',
|
services: ['跌倒风险评估', '康复训练指导', '用药安全管理', '定期上门访视', '紧急联系服务', '心理健康关怀'],
|
price: 499,
|
color: 'warning'
|
},
|
'4': {
|
name: '孕产妇保健包',
|
services: ['孕期健康管理', '产后康复指导', '新生儿护理咨询', '营养膳食建议', '心理情绪支持'],
|
price: 399,
|
color: 'danger'
|
},
|
'5': {
|
name: '儿童保健包',
|
services: ['生长发育监测', '疫苗接种管理', '常见病防治', '营养指导', '早期教育咨询'],
|
price: 199,
|
color: 'primary'
|
}
|
}
|
}
|
},
|
created() {
|
this.getList()
|
this.calculateStats()
|
},
|
methods: {
|
// 生成更真实的模拟数据
|
// 精简后的模拟数据生成方法
|
// 优化后的模拟数据生成方法
|
generateMockData() {
|
const mockData = []
|
|
// 使用您提供的真实姓名列表
|
const patientNames = [
|
'李肇芬', '卢木仲', '李成白', '方兆玉', '刘翊惠', '丁汉臻', '吴佳瑞', '舒绿珮',
|
'周白芷', '张姿妤', '张虹伦', '周琼玟', '倪怡芳', '郭贵妃', '杨佩芳', '黄文旺',
|
'黄盛玫', '郑丽青', '许智云', '张孟涵', '李小爱', '王恩龙', '朱政廷', '邓诗涵',
|
'陈政倩', '吴俊伯', '阮馨学', '翁惠珠', '吴思翰', '林佩玲'
|
]
|
|
const doctors = ['王医生', '李医生', '张医生', '刘医生', '陈医生']
|
const cities = ['北京市', '上海市', '广州市', '深圳市', '杭州市', '南京市', '成都市']
|
const areas = ['朝阳区', '海淀区', '浦东新区', '黄浦区', '天河区', '福田区', '西湖区']
|
|
// 生成约20条数据
|
for (let i = 0; i < patientNames.length; i++) {
|
const packageId = (i % 5) + 1 + ''
|
const packageInfo = this.servicePackages[packageId]
|
|
// 生成更合理的签约时间(过去1年内)
|
const signDate = this.generateRandomDate('2023-12-01', '2024-11-30')
|
const contractPeriod = [1, 2][i % 2] // 1年或2年合同
|
const expireDate = this.addYears(signDate, contractPeriod)
|
const remainingDays = this.calculateRemainingDays(expireDate)
|
const contractStatus = this.getContractStatus(expireDate, remainingDays)
|
|
// 生成更真实的电话号码和身份证号
|
const phonePrefix = ['138', '139', '150', '151', '152', '186', '187', '188']
|
const phone = `${phonePrefix[i % phonePrefix.length]}${this.padNumber(1000 + i * 37, 4)}${this.padNumber(i % 100, 2)}`
|
|
// 生成合理的年龄(20-80岁)
|
const age = 20 + (i % 60)
|
const birthYear = new Date().getFullYear() - age
|
const idCard = `11010${birthYear}${this.padNumber(1 + (i % 12), 2)}${this.padNumber(1 + (i % 28), 2)}${this.padNumber(i % 1000, 3)}X`
|
|
mockData.push({
|
id: `P${2024000 + i}`,
|
patientName: patientNames[i],
|
gender: i % 2 === 0 ? '男' : '女',
|
age: age,
|
phone: phone,
|
idCard: idCard,
|
doctorName: doctors[i % doctors.length],
|
servicePackageId: packageId,
|
servicePackage: packageInfo.name,
|
services: packageInfo.services,
|
contractPeriod: contractPeriod,
|
signDate: signDate,
|
expireDate: expireDate,
|
remainingDays: remainingDays,
|
contractStatus: contractStatus,
|
address: `${cities[i % cities.length]}${areas[i % areas.length]}${this.generateStreet(i)}` // 新增地址字段
|
})
|
}
|
|
return mockData
|
},
|
|
// 新增辅助方法:生成街道地址
|
generateStreet(index) {
|
const streets = [
|
'中山路123号', '人民路456号', '解放路789号', '建设路101号', '和平路202号',
|
'新华路303号', '光明路404号', '幸福路505号', '团结路606号', '文明路707号'
|
]
|
return streets[index % streets.length]
|
},
|
|
// 辅助方法
|
generateRandomDate(start, end) {
|
const startDate = new Date(start).getTime()
|
const endDate = new Date(end).getTime()
|
const randomTime = startDate + Math.random() * (endDate - startDate)
|
return new Date(randomTime).toISOString().split('T')[0]
|
},
|
|
addYears(date, years) {
|
const result = new Date(date)
|
result.setFullYear(result.getFullYear() + years)
|
return result.toISOString().split('T')[0]
|
},
|
|
calculateRemainingDays(expireDate) {
|
const today = new Date()
|
const expire = new Date(expireDate)
|
const diffTime = expire - today
|
return Math.ceil(diffTime / (1000 * 60 * 60 * 24))
|
},
|
|
getContractStatus(expireDate, remainingDays) {
|
if (remainingDays < 0) return 3 // 已到期
|
if (remainingDays <= 30) return 2 // 即将到期
|
return 1 // 正常服务中
|
},
|
|
padNumber(num, length) {
|
return num.toString().padStart(length, '0')
|
},
|
|
// 获取患者列表
|
async getList() {
|
this.loading = true
|
try {
|
// 模拟API调用延迟
|
await new Promise(resolve => setTimeout(resolve, 500))
|
|
const allData = this.generateMockData()
|
// 简单的本地筛选
|
let filteredData = allData.filter(item => {
|
if (this.queryParams.patientName &&
|
!item.patientName.includes(this.queryParams.patientName)) {
|
return false
|
}
|
if (this.queryParams.contractStatus &&
|
item.contractStatus !== parseInt(this.queryParams.contractStatus)) {
|
return false
|
}
|
if (this.queryParams.servicePackage &&
|
item.servicePackageId !== this.queryParams.servicePackage) {
|
return false
|
}
|
return true
|
})
|
|
// 分页处理
|
const start = (this.queryParams.pageNum - 1) * this.queryParams.pageSize
|
const end = start + this.queryParams.pageSize
|
this.patientList = filteredData.slice(start, end)
|
this.total = filteredData.length
|
|
this.calculateStats()
|
} catch (error) {
|
console.error('获取数据失败:', error)
|
this.$message.error('数据加载失败')
|
} finally {
|
this.loading = false
|
}
|
},
|
|
// 计算统计信息
|
calculateStats() {
|
const allData = this.generateMockData()
|
this.stats.total = allData.length
|
this.stats.active = allData.filter(item => item.contractStatus === 1).length
|
this.stats.expiring = allData.filter(item => item.contractStatus === 2).length
|
this.stats.expired = allData.filter(item => item.contractStatus === 3).length
|
},
|
|
// 状态相关方法
|
getStatusText(status) {
|
const statusMap = { 1: '正常服务中', 2: '即将到期', 3: '已到期', 4: '已终止' }
|
return statusMap[status] || '未知'
|
},
|
|
getStatusType(status) {
|
const typeMap = { 1: 'success', 2: 'warning', 3: 'danger', 4: 'info' }
|
return typeMap[status] || 'info'
|
},
|
|
getPackageType(packageId) {
|
const typeMap = { '1': 'info', '2': 'success', '3': 'warning', '4': 'danger', '5': 'primary' }
|
return typeMap[packageId] || 'info'
|
},
|
|
getRemainingDaysClass(days) {
|
if (days < 0) return 'expired'
|
if (days <= 30) return 'expiring'
|
return 'normal'
|
},
|
|
// 操作方法
|
handleQuery() {
|
this.queryParams.pageNum = 1
|
this.getList()
|
},
|
|
resetQuery() {
|
this.queryParams = {
|
pageNum: 1,
|
pageSize: 10,
|
patientName: undefined,
|
contractStatus: undefined,
|
servicePackage: undefined
|
}
|
this.handleQuery()
|
},
|
|
handleSelectionChange(selection) {
|
this.ids = selection.map(item => item.id)
|
},
|
|
handleView(row) {
|
this.$message.info(`查看患者 ${row.patientName} 的详情`)
|
// 实际开发中跳转到详情页
|
// this.$router.push({ path: '/patient/contract/detail', query: { id: row.id } })
|
},
|
|
handleRenew(row) {
|
this.$confirm(`确定要为患者 ${row.patientName} 办理续约吗?`, '提示', {
|
type: 'warning'
|
}).then(() => {
|
this.$message.success('续约操作成功')
|
})
|
},
|
|
handleTerminate(row) {
|
this.$confirm(`确定要终止与 ${row.patientName} 的签约关系吗?此操作不可撤销。`, '警告', {
|
type: 'error',
|
confirmButtonText: '确定终止',
|
cancelButtonText: '取消'
|
}).then(() => {
|
this.$message.success('签约关系已终止')
|
this.getList()
|
})
|
}
|
}
|
}
|
</script>
|
|
<style lang="scss" scoped>
|
.signed-patient-page {
|
padding: 20px;
|
|
.search-card {
|
margin-bottom: 20px;
|
}
|
|
.stats-row {
|
margin-bottom: 20px;
|
|
.stats-card {
|
.stats-content {
|
text-align: center;
|
padding: 10px;
|
|
.stats-number {
|
font-size: 28px;
|
font-weight: bold;
|
color: #409EFF;
|
margin-bottom: 8px;
|
}
|
|
.stats-label {
|
font-size: 14px;
|
color: #666;
|
}
|
}
|
}
|
}
|
|
.patient-info {
|
.patient-name {
|
font-weight: 600;
|
margin-bottom: 4px;
|
}
|
|
.patient-detail {
|
font-size: 12px;
|
color: #666;
|
}
|
}
|
|
.expired {
|
color: #F56C6C;
|
font-weight: bold;
|
}
|
|
.expiring {
|
color: #E6A23C;
|
font-weight: bold;
|
}
|
|
.normal {
|
color: #67C23A;
|
}
|
}
|
</style>
|