| | |
| | | <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-option |
| | | v-for="pkg in packageOptions" |
| | | :key="pkg.id" |
| | | :label="pkg.name" |
| | | :value="pkg.id" |
| | | /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | |
| | | <el-card class="stats-card" shadow="hover"> |
| | | <div class="stats-content"> |
| | | <div class="stats-number">{{ stats.total }}</div> |
| | | <div class="stats-label">总签约患者</div> |
| | | <div class="stats-label">总签约儿童</div> |
| | | </div> |
| | | </el-card> |
| | | </el-col> |
| | |
| | | :default-sort="{prop: 'signDate', order: 'descending'}" |
| | | > |
| | | <el-table-column type="selection" width="55" /> |
| | | <el-table-column label="患者信息" min-width="180" fixed> |
| | | <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 style="color: #909399; font-size: 12px; margin-top: 2px;"> |
| | | {{ scope.row.applicableAge }} |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </template> |
| | |
| | | }, |
| | | total: 0, |
| | | patientList: [], |
| | | // 服务套餐配置 |
| | | // 儿童服务套餐配置 [1,3,5](@ref) |
| | | servicePackages: { |
| | | '1': { |
| | | name: '基础健康管理包', |
| | | services: ['年度健康评估', '在线健康咨询', '健康档案管理', '定期健康提醒'], |
| | | name: '婴幼儿健康基础包', |
| | | description: '为0-3岁婴幼儿提供全面的健康监测与发育指导', |
| | | services: ['新生儿家庭访视', '定期体格检查与发育评估', '血常规检测', '听力筛查', '喂养与护理指导', '预防接种服务', '中医保健指导'], |
| | | price: 0, |
| | | color: 'info' |
| | | color: 'primary', |
| | | applicableAge: '0-3岁' |
| | | }, |
| | | '2': { |
| | | name: '慢性病管理包', |
| | | services: ['专属医生服务', '用药指导管理', '定期随访监测', '个性化康复计划', '紧急医疗咨询'], |
| | | price: 299, |
| | | color: 'success' |
| | | name: '学龄前儿童健康包', |
| | | description: '关注3-6岁儿童生长发育、常见病预防及习惯养成', |
| | | services: ['生长发育评估', '视力筛查与口腔保健', '血常规检查', '合理膳食与行为指导', '疾病预防与健康干预', '中医饮食调养指导'], |
| | | price: 0, |
| | | color: 'success', |
| | | applicableAge: '3-6岁' |
| | | }, |
| | | '3': { |
| | | name: '老年人健康包', |
| | | services: ['跌倒风险评估', '康复训练指导', '用药安全管理', '定期上门访视', '紧急联系服务', '心理健康关怀'], |
| | | price: 499, |
| | | color: 'warning' |
| | | name: '学龄儿童综合健康包', |
| | | description: '为7-12岁儿童提供学习期健康保障与发展支持', |
| | | services: ['年度健康检查', '心理行为发育评估', '科学用眼与口腔保健', '合理膳食指导', '健康生活方式干预', '专家转诊绿色通道'], |
| | | price: 0, |
| | | color: 'warning', |
| | | applicableAge: '7-12岁' |
| | | }, |
| | | '4': { |
| | | name: '孕产妇保健包', |
| | | services: ['孕期健康管理', '产后康复指导', '新生儿护理咨询', '营养膳食建议', '心理情绪支持'], |
| | | price: 399, |
| | | color: 'danger' |
| | | name: '青少年健康支持包', |
| | | description: '针对13-18岁青少年青春期特点的健康管理', |
| | | services: ['青春期健康教育', '年度健康评估', '心理健康支持', '健康风险行为干预', '个性化健康方案', '优先预约检查服务'], |
| | | price: 0, |
| | | color: 'danger', |
| | | applicableAge: '13-18岁' |
| | | }, |
| | | '5': { |
| | | name: '儿童保健包', |
| | | services: ['生长发育监测', '疫苗接种管理', '常见病防治', '营养指导', '早期教育咨询'], |
| | | price: 199, |
| | | color: 'primary' |
| | | name: '儿童营养与生长发育增值包', |
| | | description: '针对肥胖、营养不良等问题的专项管理', |
| | | services: ['微量元素测定', '骨密度检测', '个性化膳食方案', '生长发育专项评估', '运动处方指导', '定期营养监测'], |
| | | price: 150, |
| | | color: 'info', |
| | | applicableAge: '3-18岁' |
| | | }, |
| | | '6': { |
| | | name: '儿童中医特色保健包', |
| | | description: '运用中医药方法增强儿童体质', |
| | | services: ['中医体质辨识', '三伏贴服务', '小儿推拿', '耳穴治疗', '防感香囊', '食疗指导'], |
| | | price: 200, |
| | | color: 'primary', |
| | | applicableAge: '0-6岁' |
| | | } |
| | | } |
| | | }, |
| | | packageOptions: [] |
| | | } |
| | | }, |
| | | created() { |
| | | this.initPackageOptions() |
| | | this.getList() |
| | | this.calculateStats() |
| | | }, |
| | | methods: { |
| | | // 生成更真实的模拟数据 |
| | | // 精简后的模拟数据生成方法 |
| | | // 优化后的模拟数据生成方法 |
| | | generateMockData() { |
| | | const mockData = [] |
| | | // 初始化套餐选项 |
| | | initPackageOptions() { |
| | | this.packageOptions = Object.keys(this.servicePackages).map(key => ({ |
| | | id: key, |
| | | name: this.servicePackages[key].name |
| | | })) |
| | | }, |
| | | |
| | | // 使用您提供的真实姓名列表 |
| | | const patientNames = [ |
| | | '李肇芬', '卢木仲', '李成白', '方兆玉', '刘翊惠', '丁汉臻', '吴佳瑞', '舒绿珮', |
| | | '周白芷', '张姿妤', '张虹伦', '周琼玟', '倪怡芳', '郭贵妃', '杨佩芳', '黄文旺', |
| | | '黄盛玫', '郑丽青', '许智云', '张孟涵', '李小爱', '王恩龙', '朱政廷', '邓诗涵', |
| | | '陈政倩', '吴俊伯', '阮馨学', '翁惠珠', '吴思翰', '林佩玲' |
| | | ] |
| | | // 生成模拟数据 |
| | | generateMockData() { |
| | | const mockData = [] |
| | | |
| | | const doctors = ['王医生', '李医生', '张医生', '刘医生', '陈医生'] |
| | | const cities = ['北京市', '上海市', '广州市', '深圳市', '杭州市', '南京市', '成都市'] |
| | | const areas = ['朝阳区', '海淀区', '浦东新区', '黄浦区', '天河区', '福田区', '西湖区'] |
| | | // 使用儿童姓名列表 |
| | | const patientNames = [ |
| | | '李小宝', '张小明', '王雨欣', '刘浩然', '陈思琪', '杨宇航', '黄诗涵', '赵天宇', |
| | | '周小萌', '吴俊杰', '郑雅雯', '孙沐辰', '朱雨萱', '马浩宇', '胡可馨', '林俊熙', |
| | | '郭子轩', '何欣怡', '高天佑', '梁静怡', '罗浩然', '宋雨泽', '唐语嫣', '许博文', |
| | | '谢欣妍', '冯子默', '董雨桐', '萧天乐', '曹心怡', '袁嘉豪' |
| | | ] |
| | | |
| | | // 生成约20条数据 |
| | | for (let i = 0; i < patientNames.length; i++) { |
| | | const packageId = (i % 5) + 1 + '' |
| | | const packageInfo = this.servicePackages[packageId] |
| | | const doctors = ['王医生', '李医生', '张医生', '刘医生', '陈医生'] |
| | | const cities = ['北京市', '上海市', '广州市', '深圳市', '杭州市', '南京市', '成都市'] |
| | | const areas = ['朝阳区', '海淀区', '浦东新区', '黄浦区', '天河区', '福田区', '西湖区'] |
| | | |
| | | // 生成更合理的签约时间(过去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) |
| | | for (let i = 0; i < patientNames.length; i++) { |
| | | const packageId = (i % 6) + 1 + '' |
| | | const packageInfo = this.servicePackages[packageId] |
| | | |
| | | // 生成更真实的电话号码和身份证号 |
| | | 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)}` |
| | | // 根据套餐适用年龄生成合理的实际年龄 |
| | | let age |
| | | switch(packageInfo.applicableAge) { |
| | | case '0-3岁': |
| | | age = Math.floor(Math.random() * 3) + 1 |
| | | break |
| | | case '3-6岁': |
| | | age = Math.floor(Math.random() * 3) + 3 |
| | | break |
| | | case '7-12岁': |
| | | age = Math.floor(Math.random() * 6) + 7 |
| | | break |
| | | case '13-18岁': |
| | | age = Math.floor(Math.random() * 6) + 13 |
| | | break |
| | | default: |
| | | age = Math.floor(Math.random() * 18) + 1 |
| | | } |
| | | |
| | | // 生成合理的年龄(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` |
| | | // 生成签约时间(过去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) |
| | | |
| | | 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)}` // 新增地址字段 |
| | | }) |
| | | } |
| | | // 生成电话号码(使用家长电话) |
| | | 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)}` |
| | | |
| | | return mockData |
| | | }, |
| | | // 生成儿童身份证号 |
| | | 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` |
| | | |
| | | // 新增辅助方法:生成街道地址 |
| | | generateStreet(index) { |
| | | const streets = [ |
| | | '中山路123号', '人民路456号', '解放路789号', '建设路101号', '和平路202号', |
| | | '新华路303号', '光明路404号', '幸福路505号', '团结路606号', '文明路707号' |
| | | ] |
| | | return streets[index % streets.length] |
| | | }, |
| | | mockData.push({ |
| | | id: `C${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, |
| | | applicableAge: packageInfo.applicableAge, |
| | | 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() |
| | |
| | | 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)) { |
| | |
| | | }, |
| | | |
| | | getPackageType(packageId) { |
| | | const typeMap = { '1': 'info', '2': 'success', '3': 'warning', '4': 'danger', '5': 'primary' } |
| | | const typeMap = { |
| | | '1': 'primary', '2': 'success', '3': 'warning', |
| | | '4': 'danger', '5': 'info', '6': 'primary' |
| | | } |
| | | return typeMap[packageId] || 'info' |
| | | }, |
| | | |
| | |
| | | }, |
| | | |
| | | handleView(row) { |
| | | this.$message.info(`查看患者 ${row.patientName} 的详情`) |
| | | this.$message.info(`查看儿童 ${row.patientName} 的详情`) |
| | | // 实际开发中跳转到详情页 |
| | | // this.$router.push({ path: '/patient/contract/detail', query: { id: row.id } }) |
| | | }, |
| | | |
| | | handleRenew(row) { |
| | | this.$confirm(`确定要为患者 ${row.patientName} 办理续约吗?`, '提示', { |
| | | this.$confirm(`确定要为儿童 ${row.patientName} 办理续约吗?`, '提示', { |
| | | type: 'warning' |
| | | }).then(() => { |
| | | this.$message.success('续约操作成功') |
| | |
| | | |
| | | .search-card { |
| | | margin-bottom: 20px; |
| | | |
| | | ::v-deep .el-form-item { |
| | | margin-bottom: 0; |
| | | } |
| | | } |
| | | |
| | | .stats-row { |
| | |
| | | .patient-name { |
| | | font-weight: 600; |
| | | margin-bottom: 4px; |
| | | color: #2c3e50; |
| | | } |
| | | |
| | | .patient-detail { |
| | | font-size: 12px; |
| | | color: #666; |
| | | line-height: 1.4; |
| | | } |
| | | } |
| | | |
| | |
| | | color: #67C23A; |
| | | } |
| | | } |
| | | |
| | | // 响应式设计 |
| | | @media (max-width: 768px) { |
| | | .signed-patient-page { |
| | | padding: 10px; |
| | | |
| | | .search-card { |
| | | ::v-deep .el-col { |
| | | margin-bottom: 10px; |
| | | } |
| | | } |
| | | |
| | | .stats-row { |
| | | .el-col { |
| | | margin-bottom: 10px; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | </style> |