feat(i18n): migrate nursing, inpatient, mobile nursing pages + billing remaining components to vue-i18n

This commit is contained in:
2026-06-24 22:37:08 +08:00
parent 1a0e6aabb4
commit 24ea1c9e1a
19 changed files with 2387 additions and 2536 deletions

View File

@@ -1097,5 +1097,477 @@
"statusCompleted": "Completed",
"statusCancelled": "Cancelled",
"statusInProgress": "In Progress"
},
"nursing": {
"assessment": {
"encounterId": "Encounter ID",
"assessmentType": "Assessment Type",
"assessmentTool": "Assessment Tool",
"score": "Score",
"riskLevel": "Risk Level",
"assessor": "Assessor",
"assessmentTime": "Assessment Time",
"search": "Search",
"painTitle": "Pain Assessment NRS/VAS",
"patientId": "Patient ID",
"patientName": "Patient Name",
"painScore": "Pain Score",
"detail": "Assessment Detail",
"assessorName": "Assessor",
"submitAssessment": "Submit Assessment",
"reset": "Reset",
"assessmentRecords": "Assessment Records",
"detailPlaceholder": "Enter pain location, nature, duration, etc.",
"submitSuccess": "Assessment submitted successfully",
"submitFailed": "Submission failed",
"getRecordsFailed": "Failed to get records",
"nutritionTitle": "Nutrition Risk Screening NRS2002",
"totalScore": "Total Score",
"submitScreening": "Submit Screening",
"screeningRecords": "Screening Records",
"screeningSubmitSuccess": "Screening submitted successfully",
"pipeTitle": "Tube Dislodgement Risk Assessment",
"interventionContent": "Intervention Content",
"nurseName": "Nurse Name",
"interventionPlaceholder": "Enter assessment content, e.g.: high risk, medium risk, low risk, activity, fixation keywords",
"nursePlaceholder": "Enter nurse name",
"createTime": "Created Time",
"valEncounterId": "Please enter encounter ID",
"valPatientId": "Please enter patient ID",
"valPatientName": "Please enter patient name",
"valPainScore": "Please enter pain score",
"valAssessor": "Please enter assessor",
"valTotalScore": "Please enter total score",
"valInterventionContent": "Please enter assessment content",
"valNurseName": "Please enter nurse name",
"valTemperature": "Please enter temperature",
"valPulse": "Please enter pulse",
"valSystolicBp": "Please enter systolic BP",
"valDiastolicBp": "Please enter diastolic BP",
"valDrugName": "Please enter drug name",
"valBarcode": "Please enter barcode"
},
"mobile": {
"patientList": "Patient List",
"searchPlaceholder": "Search name/bed no.",
"bedNo": "Bed No.",
"nursingLevel": "Nursing Level",
"condition": "Condition",
"diagnosis": "Diagnosis",
"noPatients": "No patients",
"vitalSignEntry": "Vital Sign Entry",
"patient": "Patient",
"recordDate": "Record Date",
"selectDate": "Select Date",
"recordHour": "Hour",
"temperature": "Temperature",
"pulse": "Pulse",
"respiration": "Respiration",
"systolicBp": "Systolic BP",
"diastolicBp": "Diastolic BP",
"painScore": "Pain Score",
"consciousness": "Consciousness",
"consciousClear": "Alert",
"consciousDrowsy": "Drowsy",
"consciousConfused": "Confused",
"consciousStupor": "Stupor",
"consciousComaShallow": "Shallow Coma",
"consciousComaDeep": "Deep Coma",
"inputVolume": "Intake",
"outputVolume": "Output",
"save": "Save",
"saveSuccess": "Saved successfully",
"saveFailed": "Save failed",
"unitTimesPerMin": "times/min",
"nursingAssessment": "Nursing Assessment",
"bradenPressure": "Braden Pressure Ulcer",
"morseFall": "Morse Fall",
"nrs2002Nutrition": "NRS2002 Nutrition",
"totalScoreLabel": "Total Score",
"riskLevelLabel": "Risk Level",
"assessmentRemark": "Assessment Remark",
"optionalRemark": "Optional remark",
"submitAssessmentBtn": "Submit Assessment",
"assessmentRecordsLabel": "Assessment Records",
"noAssessmentRecords": "No assessment records",
"scoreLabel": "Score",
"completeAssessment": "Please complete all assessment items",
"assessmentSubmitSuccess": "Assessment submitted successfully",
"assessmentSubmitFailed": "Submission failed",
"highRisk": "High Risk",
"mediumRisk": "Medium Risk",
"lowRisk": "Low Risk",
"normal": "Normal",
"unknown": "Unknown",
"bradenSensory": "Sensory Perception",
"bradenMoisture": "Moisture",
"bradenActivity": "Activity",
"bradenMobility": "Mobility",
"bradenNutrition": "Nutrition",
"bradenFriction": "Friction and Shear",
"sensoryComplete": "Completely Limited",
"sensorySevere": "Severely Limited",
"sensoryMild": "Slightly Limited",
"sensoryNone": "No Impairment",
"moisturePersistent": "Constantly Moist",
"moistureFrequent": "Often Moist",
"moistureOccasional": "Occasionally Moist",
"moistureRare": "Rarely Moist",
"activityBed": "Bedfast",
"activityChair": "Chairfast",
"activityOccasionalWalk": "Walks Occasionally",
"activityFrequentWalk": "Walks Frequently",
"mobilityNone": "Completely Immobile",
"mobilitySevere": "Very Limited",
"mobilityMild": "Slightly Limited",
"mobilityUnlimited": "No Limitation",
"nutritionVeryPoor": "Very Poor",
"nutritionProbablyInsufficient": "Probably Inadequate",
"nutritionAdequate": "Adequate",
"nutritionRich": "Excellent",
"frictionProblem": "Problem",
"frictionPotential": "Potential Problem",
"frictionNone": "No Apparent Problem",
"morseFallHistory": "Fall History",
"morseDiagnosis": "More Than One Medical Diagnosis",
"morseAmbulation": "Ambulation Aid",
"morseIV": "IV Therapy",
"morseGait": "Gait",
"morseMental": "Mental Status",
"yes": "Yes",
"no": "No",
"ambulationBedWheelchair": "Bed/Wheelchair",
"ambulationCrutch": "Crutch/Cane/Walker",
"ambulationFurniture": "Furniture",
"gaitNormal": "Normal/Bed/Wheelchair",
"gaitWeak": "Weak",
"gaitImpaired": "Impaired",
"mentalCorrect": "Correct Self-assessment",
"mentalOverestimate": "Overestimates/Forgets Limitations",
"nrs2002BMI": "BMI(kg/m²)",
"nrs2002WeightLoss": "Weight Loss",
"nrs2002Intake": "Dietary Intake",
"nrs2002Illness": "Severity of Illness",
"intakeNone": "None",
"intakePoor": "Poor",
"intakeModerate": "Moderate",
"intakeGood": "Good",
"illnessSurgery": "Major Surgery/Trauma",
"illnessFracture": "Hip Fracture",
"illnessAcute": "Chronic Disease Acute",
"illnessMild": "None/Mild",
"orderExecuting": "Executing",
"orderProofread": "Proofread",
"all": "All",
"scanExecute": "Scan to Execute",
"type": "Type",
"frequency": "Frequency",
"dosage": "Dosage",
"orderDoctor": "Ordering Doctor",
"execute": "Execute",
"noOrders": "No orders",
"scanExecuteTitle": "Scan to Execute",
"barcode": "Barcode",
"barcodePlaceholder": "Scan or enter barcode",
"confirmExecute": "Confirm Execute",
"confirmExecuteOrder": "Confirm execute order",
"executeSuccess": "Executed successfully",
"executeFailed": "Execution failed",
"orderMatchFailed": "No matching order found",
"infusionManagement": "Infusion Management",
"startInfusion": "Start Infusion",
"infusionSpeed": "Infusion Rate",
"totalVolume": "Total Volume (ml)",
"patrolNurse": "Patrol Nurse",
"executeNurse": "Execute Nurse",
"confirmStart": "Confirm Start",
"infusionPatrol": "Infusion Patrol",
"dripRateLabel": "Drip Rate (drops/min)",
"patencyStatus": "Patency Status",
"adverseReaction": "Adverse Reaction",
"emptyLeaveBlank": "Leave blank if none",
"patrolNurseLabel": "Patrol Nurse",
"confirmRecord": "Confirm Record",
"infusionStarted": "Infusion started",
"operationFailed": "Operation failed",
"patrolRecordSaved": "Patrol record saved",
"recordFailed": "Record failed",
"patrolRecord": "Patrol Record",
"noInfusionRecords": "No infusion records",
"startInfusionTitle": "Start Infusion",
"drugName": "Drug Name",
"drugNamePlaceholder": "Enter drug name",
"infusionSpeedPlaceholder": "e.g.: 40 drops/min",
"occluded": "Occluded",
"phlebitis": "Phlebitis",
"vitalTrend": "Vital Sign Trend",
"days3": "3 Days",
"days7": "7 Days",
"days14": "14 Days",
"temperatureChart": "Temperature (°C)",
"pulseChart": "Pulse (beats/min)",
"bpChart": "Blood Pressure (mmHg)",
"respirationChart": "Respiration (breaths/min)",
"noData": "No data"
},
"quality": {
"title": "Nursing Quality Indicator Management",
"refresh": "Refresh",
"addIndicator": "Add Indicator",
"collectIndicator": "Collect Indicators",
"exportReport": "Export Report",
"totalIndicators": "Total Indicators",
"metCount": "Met",
"unmetCount": "Unmet",
"meetRate": "Meet Rate",
"deptCount": "Departments",
"avgMeetRate": "Avg Meet Rate",
"meetRateTrend": "Meet Rate Trend",
"deptMeetCompare": "Department Meet Comparison",
"categoryDistribution": "Category Distribution",
"unmetWarning": "Unmet Indicator Warning",
"indicatorName": "Indicator Name",
"department": "Department",
"targetValue": "Target",
"actualValue": "Actual",
"gap": "Gap",
"indicatorCategory": "Indicator Category",
"basicNursing": "Basic Nursing",
"specializedNursing": "Specialized Nursing",
"nursingSafety": "Nursing Safety",
"nursingDocumentation": "Nursing Documentation",
"sterilization": "Sterilization",
"internalMedicine": "Internal Medicine",
"surgery": "Surgery",
"obstetrics": "Obstetrics",
"pediatrics": "Pediatrics",
"icu": "ICU",
"emergency": "Emergency",
"query": "Query",
"resetQuery": "Reset",
"indicatorCode": "Code",
"category": "Category",
"metStatus": "Status",
"met": "Met",
"unmet": "Unmet",
"statDate": "Stat Date",
"remark": "Remark",
"addDialog": "Add Nursing Quality Indicator",
"indicatorCodePlaceholder": "e.g.: NQ001",
"indicatorNamePlaceholder": "Enter indicator name",
"selectCategory": "Select",
"departmentPlaceholder": "Enter department",
"selectDate": "Select Date",
"cancel": "Cancel",
"confirm": "Confirm",
"addSuccess": "Added successfully",
"exportDev": "Export feature in development",
"collectSuccess": "Collected {count} indicators",
"collectFailed": "Collection failed",
"monthLabels": ["Jan", "Feb", "Mar", "Apr", "May", "Jun"]
},
"execution": {
"title": "Inpatient Nurse Execution",
"scanTab": "Scan Execution",
"addScan": "Add Scan",
"patient": "Patient",
"type": "Type",
"order": "Order",
"barcode": "Barcode",
"result": "Result",
"success": "Success",
"failed": "Failed",
"nurse": "Nurse",
"time": "Time",
"handoffTab": "Shift Handoff",
"addHandoff": "Add Handoff",
"keyPatientAlert": "Key Patient Alert",
"pending": "Pending",
"ward": "Ward",
"shift": "Shift",
"handoffNurse": "Handoff Nurse",
"receiveNurse": "Receive Nurse",
"patientCount": "Patients",
"criticalCount": "Critical",
"keyPatients": "Key Patients",
"status": "Status",
"draft": "Draft",
"handedOff": "Handed Off",
"confirmed": "Confirmed",
"operation": "Operation",
"confirm": "Confirm",
"confirmedMsg": "Confirmed",
"infusionTab": "Infusion Patrol",
"addPatrol": "Add Patrol",
"drug": "Drug",
"dripRate": "Drip Rate",
"patency": "Patency",
"patrolNurseLabel": "Patrol Nurse",
"normal": "Normal",
"abnormal": "Abnormal"
},
"enhanced": {
"title": "Enhanced Nursing Assessment",
"reminderTab": "Assessment Reminders",
"carePlanTab": "Care Plans",
"qualityTab": "Quality Indicators",
"overdue": "Overdue",
"completedAssessment": "Completed",
"patient": "Patient",
"assessmentType": "Assessment Type",
"frequencyHours": "Frequency (hrs)",
"nextDeadline": "Next Deadline",
"status": "Status",
"pendingAssessment": "Pending",
"assessed": "Assessed",
"overdueStatus": "Overdue",
"completeAssessment": "Complete",
"addCarePlan": "Add Care Plan",
"nursingDiagnosis": "Nursing Diagnosis",
"nursingGoal": "Nursing Goal",
"nursingIntervention": "Nursing Intervention",
"newStatus": "New",
"executing": "Executing",
"completed": "Completed",
"stopped": "Stopped",
"evaluate": "Evaluate",
"totalReminders": "Total Reminders",
"totalCompleted": "Completed",
"totalOverdue": "Overdue",
"totalPlans": "Total Plans",
"completedPlans": "Completed Plans",
"addCarePlanTitle": "Add Care Plan",
"encounterId": "Encounter ID",
"cancel": "Cancel",
"save": "Save",
"evaluateTitle": "Evaluate Care Plan",
"evaluatePrompt": "Enter evaluation result",
"addSuccess": "Added successfully",
"assessmentComplete": "Assessment completed",
"evaluateComplete": "Evaluation completed",
"fallRisk": "Fall Risk",
"pressureUlcer": "Pressure Ulcer Risk",
"nutrition": "Nutrition Screening",
"pain": "Pain Assessment",
"pipeline": "Tube Assessment"
},
"enhancedAssessment": {
"title": "Specialized Nursing Assessment",
"refreshStats": "Refresh Stats",
"exportReport": "Export Report",
"totalAssessments": "Total",
"highRiskPatients": "High Risk",
"mediumRiskPatients": "Medium Risk",
"intervened": "Intervened",
"pendingAssessment": "Pending",
"assessmentRate": "Assessment Rate",
"bradenTab": "Pressure Ulcer (Braden)",
"newBraden": "New Braden Assessment",
"morseTab": "Fall Assessment (Morse)",
"newMorse": "New Morse Assessment",
"nrs2002Tab": "Nutrition Screening (NRS2002)",
"newNrs2002": "New NRS2002 Assessment",
"painTab": "Pain Assessment",
"newPain": "New Pain Assessment",
"tubeTab": "Tube Risk Assessment",
"newTube": "New Tube Assessment",
"interventionTab": "Nursing Interventions",
"patient": "Patient",
"totalScore": "Total Score",
"risk": "Risk",
"assessor": "Assessor",
"assessmentTime": "Assessment Time",
"remark": "Remark",
"score": "Score",
"painLevel": "Pain Level",
"interventionType": "Intervention Type",
"interventionContent": "Intervention Content",
"status": "Status",
"executor": "Executor",
"executeTime": "Execute Time",
"high": "High",
"medium": "Medium",
"low": "Low",
"highRisk": "High Risk",
"mediumRisk": "Medium Risk",
"lowRisk": "Low Risk",
"severe": "Severe",
"moderatePain": "Moderate",
"mildPain": "Mild",
"pendingExec": "Pending",
"patientName": "Patient Name",
"patientNamePlaceholder": "Enter patient name",
"encounterId": "Encounter ID",
"encounterIdPlaceholder": "Enter encounter ID",
"bradenScale": "Braden Pressure Ulcer Risk Scale",
"sensation": "Sensation",
"moisture": "Moisture",
"activity": "Activity",
"mobility": "Mobility",
"nutrition": "Nutrition",
"friction": "Friction",
"sensationComplete": "Completely Limited",
"sensationSevere": "Severely Limited",
"sensationMild": "Slightly Limited",
"sensationIntact": "No Impairment",
"moisturePersistent": "Constantly Moist",
"moistureWet": "Moist",
"moistureOccasional": "Occasionally Moist",
"moistureRare": "Rarely Moist",
"activityBed": "Bedfast",
"activityWheelchair": "Chairfast",
"activityOccasionalWalk": "Walks Occasionally",
"activityFrequentWalk": "Walks Frequently",
"mobilityNone": "Completely Immobile",
"mobilitySevere": "Very Limited",
"mobilityMild": "Slightly Limited",
"mobilityUnlimited": "No Limitation",
"nutritionVeryPoor": "Very Poor",
"nutritionProbablyInsufficient": "Probably Inadequate",
"nutritionAdequate": "Adequate",
"nutritionRich": "Excellent",
"frictionProblem": "Problem",
"frictionPotential": "Potential Problem",
"frictionNone": "No Apparent Problem",
"totalScoreLabel": "Total Score",
"morseScale": "Morse Fall Risk Scale",
"fallHistory": "Fall History",
"diagnosis": "Diagnosis",
"ambulation": "Ambulation",
"ivTherapy": "IV Therapy",
"gait": "Gait",
"mentalState": "Mental State",
"no": "No",
"yes": "Yes",
"multiDiagnosis": "Yes (≥2)",
"noneOrWheelchair": "None/Bed/Wheelchair",
"crutchOrWalker": "Crutch/Walker",
"furniture": "Furniture",
"normalOrWheelchair": "Normal/Bed/Wheelchair",
"weak": "Weak",
"impaired": "Impaired",
"correctSelfAssessment": "Correct Self-assessment",
"overestimate": "Overestimates/Forgets",
"assessmentRemark": "Assessment Remark",
"remarkPlaceholder": "Enter assessment remark",
"cancel": "Cancel",
"submitAssessment": "Submit Assessment",
"submitSuccess": "Assessment submitted successfully",
"submitFailed": "Submission failed: {error}",
"exportDev": "Export feature in development"
},
"station": {
"inOut": "Admission/Transfer/Discharge",
"inpatientRecord": "Inpatient Record",
"orderProofread": "Order Proofread",
"orderExecution": "Order Execution",
"nursingRecord": "Nursing Record",
"tprSheet": "TPR Chart",
"inpatientBilling": "Inpatient Billing",
"drugDistribution": "Drug Distribution",
"consumableInventory": "Consumable Inventory",
"rollFee": "Roll Fee Config",
"medicineReturn": "Medicine Return"
}
}
}

View File

@@ -1097,5 +1097,477 @@
"statusCompleted": "Hoàn thành",
"statusCancelled": "Đã hủy",
"statusInProgress": "Đang xử lý"
},
"nursing": {
"assessment": {
"encounterId": "Mã khám bệnh",
"assessmentType": "Loại đánh giá",
"assessmentTool": "Công cụ đánh giá",
"score": "Điểm số",
"riskLevel": "Mức độ rủi ro",
"assessor": "Người đánh giá",
"assessmentTime": "Thời gian đánh giá",
"search": "Tìm kiếm",
"painTitle": "Đánh giá đau NRS/VAS",
"patientId": "Mã bệnh nhân",
"patientName": "Tên bệnh nhân",
"painScore": "Điểm đau",
"detail": "Chi tiết đánh giá",
"assessorName": "Người đánh giá",
"submitAssessment": "Gửi đánh giá",
"reset": "Đặt lại",
"assessmentRecords": "Hồ sơ đánh giá",
"detailPlaceholder": "Nhập vị trí đau, tính chất, thời gian...",
"submitSuccess": "Gửi đánh giá thành công",
"submitFailed": "Gửi thất bại",
"getRecordsFailed": "Lấy hồ sơ thất bại",
"nutritionTitle": "Sàng lọc nguy cơ dinh dưỡng NRS2002",
"totalScore": "Tổng điểm",
"submitScreening": "Gửi sàng lọc",
"screeningRecords": "Hồ sơ sàng lọc",
"screeningSubmitSuccess": "Gửi sàng lọc thành công",
"pipeTitle": "Đánh giá rủi ro tuột ống",
"interventionContent": "Nội dung can thiệp",
"nurseName": "Tên y tá",
"interventionPlaceholder": "Nhập nội dung đánh giá, ví dụ: rủi ro cao, trung bình, thấp...",
"nursePlaceholder": "Nhập tên y tá",
"createTime": "Thời gian tạo",
"valEncounterId": "Vui lòng nhập mã khám bệnh",
"valPatientId": "Vui lòng nhập mã bệnh nhân",
"valPatientName": "Vui lòng nhập tên bệnh nhân",
"valPainScore": "Vui lòng nhập điểm đau",
"valAssessor": "Vui lòng nhập người đánh giá",
"valTotalScore": "Vui lòng nhập tổng điểm",
"valInterventionContent": "Vui lòng nhập nội dung đánh giá",
"valNurseName": "Vui lòng nhập tên y tá",
"valTemperature": "Vui lòng nhập nhiệt độ",
"valPulse": "Vui lòng nhập mạch",
"valSystolicBp": "Vui lòng nhập huyết áp tâm thu",
"valDiastolicBp": "Vui lòng nhập huyết áp tâm trương",
"valDrugName": "Vui lòng nhập tên thuốc",
"valBarcode": "Vui lòng nhập mã vạch"
},
"mobile": {
"patientList": "Danh sách bệnh nhân",
"searchPlaceholder": "Tìm tên/số giường",
"bedNo": "Số giường",
"nursingLevel": "Cấp điều dưỡng",
"condition": "Tình trạng",
"diagnosis": "Chẩn đoán",
"noPatients": "Không có bệnh nhân",
"vitalSignEntry": "Nhập dấu hiệu sinh tồn",
"patient": "Bệnh nhân",
"recordDate": "Ngày ghi nhận",
"selectDate": "Chọn ngày",
"recordHour": "Giờ",
"temperature": "Nhiệt độ",
"pulse": "Mạch",
"respiration": "Hô hấp",
"systolicBp": "Huyết áp tâm thu",
"diastolicBp": "Huyết áp tâm trương",
"painScore": "Điểm đau",
"consciousness": "Ý thức",
"consciousClear": "Tỉnh táo",
"consciousDrowsy": "Buồn ngủ",
"consciousConfused": "Lú lẫn",
"consciousStupor": "Sopor",
"consciousComaShallow": "Hôn mê nhẹ",
"consciousComaDeep": "Hôn mê sâu",
"inputVolume": "Lượng vào",
"outputVolume": "Lượng ra",
"save": "Lưu",
"saveSuccess": "Lưu thành công",
"saveFailed": "Lưu thất bại",
"unitTimesPerMin": "lần/phút",
"nursingAssessment": "Đánh giá điều dưỡng",
"bradenPressure": "Braden loét",
"morseFall": "Morse ngã",
"nrs2002Nutrition": "NRS2002 dinh dưỡng",
"totalScoreLabel": "Tổng điểm",
"riskLevelLabel": "Mức rủi ro",
"assessmentRemark": "Ghi chú đánh giá",
"optionalRemark": "Ghi chú tùy chọn",
"submitAssessmentBtn": "Gửi đánh giá",
"assessmentRecordsLabel": "Hồ sơ đánh giá",
"noAssessmentRecords": "Chưa có hồ sơ đánh giá",
"scoreLabel": "Điểm",
"completeAssessment": "Vui lòng hoàn thành tất cả mục đánh giá",
"assessmentSubmitSuccess": "Gửi đánh giá thành công",
"assessmentSubmitFailed": "Gửi thất bại",
"highRisk": "Rủi ro cao",
"mediumRisk": "Rủi ro trung bình",
"lowRisk": "Rủi ro thấp",
"normal": "Bình thường",
"unknown": "Không rõ",
"bradenSensory": "Cảm giác",
"bradenMoisture": "Độ ẩm",
"bradenActivity": "Hoạt động",
"bradenMobility": "Di chuyển",
"bradenNutrition": "Dinh dưỡng",
"bradenFriction": "Ma sát và cắt",
"sensoryComplete": "Hoàn toàn hạn chế",
"sensorySevere": "Hạn chế nghiêm trọng",
"sensoryMild": "Hạn chế nhẹ",
"sensoryNone": "Không suy giảm",
"moisturePersistent": "Luôn ẩm",
"moistureFrequent": "Thường ẩm",
"moistureOccasional": "Thỉnh thoảng ẩm",
"moistureRare": "Hiếm khi ẩm",
"activityBed": "Nằm liệt giường",
"activityChair": "Chỉ ngồi ghế",
"activityOccasionalWalk": "Thỉnh thoảng đi bộ",
"activityFrequentWalk": "Thường xuyên đi bộ",
"mobilityNone": "Hoàn toàn bất động",
"mobilitySevere": "Rất hạn chế",
"mobilityMild": "Hạn chế nhẹ",
"mobilityUnlimited": "Không hạn chế",
"nutritionVeryPoor": "Rất kém",
"nutritionProbablyInsufficient": "Có thể không đủ",
"nutritionAdequate": "Đủ",
"nutritionRich": "Giàu",
"frictionProblem": "Có vấn đề",
"frictionPotential": "Tiềm ẩn vấn đề",
"frictionNone": "Không có vấn đề",
"morseFallHistory": "Tiền sử ngã",
"morseDiagnosis": "Nhiều chẩn đoán y khoa",
"morseAmbulation": "Phương tiện đi lại",
"morseIV": "Truyền tĩnh mạch",
"morseGait": "Bước đi",
"morseMental": "Tinh thần",
"yes": "Có",
"no": "Không",
"ambulationBedWheelchair": "Giường/Xe lăn",
"ambulationCrutch": "Nạng/Gậy/Khung đi",
"ambulationFurniture": "Bám đồ nội thất",
"gaitNormal": "Bình thường/Giường/Xe lăn",
"gaitWeak": "Yếu",
"gaitImpaired": "Suy giảm",
"mentalCorrect": "Đánh giá đúng khả năng",
"mentalOverestimate": "Đánh giá quá/Quên hạn chế",
"nrs2002BMI": "BMI(kg/m²)",
"nrs2002WeightLoss": "Sụt cân",
"nrs2002Intake": "Chế độ ăn",
"nrs2002Illness": "Mức độ nặng",
"intakeNone": "Không",
"intakePoor": "Kém",
"intakeModerate": "Trung bình",
"intakeGood": "Tốt",
"illnessSurgery": "Phẫu thuật lớn/Chấn thương",
"illnessFracture": "Gãy xương chậu",
"illnessAcute": "Bệnh mạn cấp tính",
"illnessMild": "Không/Nhẹ",
"orderExecuting": "Đang thực hiện",
"orderProofread": "Đã đối chiếu",
"all": "Tất cả",
"scanExecute": "Quét mã thực hiện",
"type": "Loại",
"frequency": "Tần suất",
"dosage": "Liều lượng",
"orderDoctor": "Bác sĩ chỉ định",
"execute": "Thực hiện",
"noOrders": "Không có y lệnh",
"scanExecuteTitle": "Quét mã thực hiện",
"barcode": "Mã vạch",
"barcodePlaceholder": "Quét hoặc nhập mã vạch",
"confirmExecute": "Xác nhận thực hiện",
"confirmExecuteOrder": "Xác nhận thực hiện y lệnh",
"executeSuccess": "Thực hiện thành công",
"executeFailed": "Thực hiện thất bại",
"orderMatchFailed": "Không tìm thấy y lệnh phù hợp",
"infusionManagement": "Quản lý truyền dịch",
"startInfusion": "Bắt đầu truyền",
"infusionSpeed": "Tốc độ truyền",
"totalVolume": "Tổng dung tích (ml)",
"patrolNurse": "Y tá tuần",
"executeNurse": "Y tá thực hiện",
"confirmStart": "Xác nhận bắt đầu",
"infusionPatrol": "Tuần truyền dịch",
"dripRateLabel": "Tốc độ giọt (giọt/phút)",
"patencyStatus": "Trạng thái thông",
"adverseReaction": "Phản ứng bất lợi",
"emptyLeaveBlank": "Để trống nếu không có",
"patrolNurseLabel": "Y tá tuần",
"confirmRecord": "Xác nhận ghi nhận",
"infusionStarted": "Đã bắt đầu truyền",
"operationFailed": "Thao tác thất bại",
"patrolRecordSaved": "Đã lưu hồ sơ tuần",
"recordFailed": "Ghi nhận thất bại",
"patrolRecord": "Hồ sơ tuần",
"noInfusionRecords": "Không có hồ sơ truyền dịch",
"startInfusionTitle": "Bắt đầu truyền",
"drugName": "Tên thuốc",
"drugNamePlaceholder": "Nhập tên thuốc",
"infusionSpeedPlaceholder": "vd: 40 giọt/phút",
"occluded": "Tắc",
"phlebitis": "Viêm tĩnh mạch",
"vitalTrend": "Xu hướng dấu hiệu sinh tồn",
"days3": "3 ngày",
"days7": "7 ngày",
"days14": "14 ngày",
"temperatureChart": "Nhiệt độ (°C)",
"pulseChart": "Mạch (lần/phút)",
"bpChart": "Huyết áp (mmHg)",
"respirationChart": "Hô hấp (lần/phút)",
"noData": "Không có dữ liệu"
},
"quality": {
"title": "Quản lý chỉ số chất lượng điều dưỡng",
"refresh": "Làm mới",
"addIndicator": "Thêm chỉ số",
"collectIndicator": "Thu thập chỉ số",
"exportReport": "Xuất báo cáo",
"totalIndicators": "Tổng chỉ số",
"metCount": "Đạt",
"unmetCount": "Chưa đạt",
"meetRate": "Tỷ lệ đạt",
"deptCount": "Số khoa",
"avgMeetRate": "TB tỷ lệ đạt",
"meetRateTrend": "Xu hướng tỷ lệ đạt",
"deptMeetCompare": "So sánh đạt theo khoa",
"categoryDistribution": "Phân bố loại chỉ số",
"unmetWarning": "Cảnh báo chỉ số chưa đạt",
"indicatorName": "Tên chỉ số",
"department": "Khoa",
"targetValue": "Mục tiêu",
"actualValue": "Thực tế",
"gap": "Chênh lệch",
"indicatorCategory": "Loại chỉ số",
"basicNursing": "ĐD cơ bản",
"specializedNursing": "ĐD chuyên khoa",
"nursingSafety": "An toàn ĐD",
"nursingDocumentation": "Hồ sơ ĐD",
"sterilization": "Khử trùng",
"internalMedicine": "Nội khoa",
"surgery": "Ngoại khoa",
"obstetrics": "Sản phụ khoa",
"pediatrics": "Nhi khoa",
"icu": "ICU",
"emergency": "Cấp cứu",
"query": "Tìm kiếm",
"resetQuery": "Đặt lại",
"indicatorCode": "Mã chỉ số",
"category": "Loại",
"metStatus": "Trạng thái",
"met": "Đạt",
"unmet": "Chưa đạt",
"statDate": "Ngày thống kê",
"remark": "Ghi chú",
"addDialog": "Thêm chỉ số chất lượng điều dưỡng",
"indicatorCodePlaceholder": "vd: NQ001",
"indicatorNamePlaceholder": "Nhập tên chỉ số",
"selectCategory": "Chọn",
"departmentPlaceholder": "Nhập khoa",
"selectDate": "Chọn ngày",
"cancel": "Hủy",
"confirm": "Xác nhận",
"addSuccess": "Thêm thành công",
"exportDev": "Chức năng xuất đang phát triển",
"collectSuccess": "Đã thu thập {count} chỉ số",
"collectFailed": "Thu thập thất bại",
"monthLabels": ["T1", "T2", "T3", "T4", "T5", "T6"]
},
"execution": {
"title": "Thực hiện y tá nội trú",
"scanTab": "Quét mã thực hiện",
"addScan": "Thêm quét",
"patient": "Bệnh nhân",
"type": "Loại",
"order": "Y lệnh",
"barcode": "Mã vạch",
"result": "Kết quả",
"success": "Thành công",
"failed": "Thất bại",
"nurse": "Y tá",
"time": "Thời gian",
"handoffTab": "Giao ca",
"addHandoff": "Thêm giao ca",
"keyPatientAlert": "Cảnh báo BN trọng điểm",
"pending": "Chờ xử lý",
"ward": "Khu",
"shift": "Ca",
"handoffNurse": "Y tá giao",
"receiveNurse": "Y tá nhận",
"patientCount": "BN",
"criticalCount": "Nguy kịch",
"keyPatients": "BN trọng điểm",
"status": "Trạng thái",
"draft": "Nháp",
"handedOff": "Đã giao",
"confirmed": "Đã xác nhận",
"operation": "Thao tác",
"confirm": "Xác nhận",
"confirmedMsg": "Đã xác nhận",
"infusionTab": "Tuần truyền dịch",
"addPatrol": "Thêm tuần",
"drug": "Thuốc",
"dripRate": "Tốc độ giọt",
"patency": "Thông",
"patrolNurseLabel": "Y tá tuần",
"normal": "Bình thường",
"abnormal": "Bất thường"
},
"enhanced": {
"title": "Đánh giá điều dưỡng nâng cao",
"reminderTab": "Nhắc nhở đánh giá",
"carePlanTab": "Kế hoạch chăm sóc",
"qualityTab": "Chỉ số chất lượng",
"overdue": "Quá hạn",
"completedAssessment": "Đã hoàn thành",
"patient": "Bệnh nhân",
"assessmentType": "Loại đánh giá",
"frequencyHours": "Tần suất (giờ)",
"nextDeadline": "Hạn tiếp theo",
"status": "Trạng thái",
"pendingAssessment": "Chờ đánh giá",
"assessed": "Đã đánh giá",
"overdueStatus": "Quá hạn",
"completeAssessment": "Hoàn thành",
"addCarePlan": "Thêm kế hoạch",
"nursingDiagnosis": "Chẩn đoán ĐD",
"nursingGoal": "Mục tiêu ĐD",
"nursingIntervention": "Biện pháp ĐD",
"newStatus": "Mới",
"executing": "Đang thực hiện",
"completed": "Hoàn thành",
"stopped": "Dừng",
"evaluate": "Đánh giá",
"totalReminders": "Tổng nhắc nhở",
"totalCompleted": "Hoàn thành",
"totalOverdue": "Quá hạn",
"totalPlans": "Tổng kế hoạch",
"completedPlans": "KH hoàn thành",
"addCarePlanTitle": "Thêm kế hoạch chăm sóc",
"encounterId": "Mã khám bệnh",
"cancel": "Hủy",
"save": "Lưu",
"evaluateTitle": "Đánh giá kế hoạch chăm sóc",
"evaluatePrompt": "Nhập kết quả đánh giá",
"addSuccess": "Thêm thành công",
"assessmentComplete": "Đánh giá hoàn thành",
"evaluateComplete": "Đánh giá hoàn thành",
"fallRisk": "Rủi ro ngã",
"pressureUlcer": "Rủi ro loét",
"nutrition": "Sàng lọc dinh dưỡng",
"pain": "Đánh giá đau",
"pipeline": "Đánh giá ống"
},
"enhancedAssessment": {
"title": "Đánh giá chuyên khoa điều dưỡng",
"refreshStats": "Làm mới TK",
"exportReport": "Xuất báo cáo",
"totalAssessments": "Tổng",
"highRiskPatients": "Rủi ro cao",
"mediumRiskPatients": "Rủi ro TB",
"intervened": "Đã can thiệp",
"pendingAssessment": "Chờ đánh giá",
"assessmentRate": "Tỷ lệ đánh giá",
"bradenTab": "Loét tì đè (Braden)",
"newBraden": "Tạo đánh giá Braden",
"morseTab": "Đánh giá ngã (Morse)",
"newMorse": "Tạo đánh giá Morse",
"nrs2002Tab": "Sàng lọc dinh dưỡng (NRS2002)",
"newNrs2002": "Tạo đánh giá NRS2002",
"painTab": "Đánh giá đau",
"newPain": "Tạo đánh giá đau",
"tubeTab": "Đánh giá rủi ro ống",
"newTube": "Tạo đánh giá ống",
"interventionTab": "Biện pháp điều dưỡng",
"patient": "Bệnh nhân",
"totalScore": "Tổng điểm",
"risk": "Rủi ro",
"assessor": "Người đánh giá",
"assessmentTime": "Thời gian đánh giá",
"remark": "Ghi chú",
"score": "Điểm",
"painLevel": "Mức độ đau",
"interventionType": "Loại biện pháp",
"interventionContent": "Nội dung biện pháp",
"status": "Trạng thái",
"executor": "Người thực hiện",
"executeTime": "Thời gian thực hiện",
"high": "Cao",
"medium": "TB",
"low": "Thấp",
"highRisk": "Rủi ro cao",
"mediumRisk": "Rủi ro TB",
"lowRisk": "Rủi ro thấp",
"severe": "Nặng",
"moderatePain": "Vừa",
"mildPain": "Nhẹ",
"pendingExec": "Chờ thực hiện",
"patientName": "Tên BN",
"patientNamePlaceholder": "Nhập tên BN",
"encounterId": "Mã khám bệnh",
"encounterIdPlaceholder": "Nhập mã khám bệnh",
"bradenScale": "Thang đánh giá rủi ro loét Braden",
"sensation": "Cảm giác",
"moisture": "Độ ẩm",
"activity": "Hoạt động",
"mobility": "Di chuyển",
"nutrition": "Dinh dưỡng",
"friction": "Ma sát",
"sensationComplete": "Hoàn toàn hạn chế",
"sensationSevere": "Hạn chế nặng",
"sensationMild": "Hạn chế nhẹ",
"sensationIntact": "Không suy giảm",
"moisturePersistent": "Luôn ẩm",
"moistureWet": "Ẩm",
"moistureOccasional": "Thỉnh thoảng ẩm",
"moistureRare": "Hiếm khi ẩm",
"activityBed": "Nằm liệt",
"activityWheelchair": "Ngồi ghế",
"activityOccasionalWalk": "Thỉnh thoảng đi",
"activityFrequentWalk": "Thường đi",
"mobilityNone": "Bất động hoàn toàn",
"mobilitySevere": "Rất hạn chế",
"mobilityMild": "Hạn chế nhẹ",
"mobilityUnlimited": "Không hạn chế",
"nutritionVeryPoor": "Rất kém",
"nutritionProbablyInsufficient": "Có thể không đủ",
"nutritionAdequate": "Đủ",
"nutritionRich": "Giàu",
"frictionProblem": "Có vấn đề",
"frictionPotential": "Tiềm ẩn",
"frictionNone": "Không vấn đề",
"totalScoreLabel": "Tổng điểm",
"morseScale": "Thang đánh giá ngã Morse",
"fallHistory": "Tiền sử ngã",
"diagnosis": "Chẩn đoán",
"ambulation": "Phương tiện",
"ivTherapy": "Truyền tĩnh mạch",
"gait": "Bước đi",
"mentalState": "Tinh thần",
"no": "Không",
"yes": "Có",
"multiDiagnosis": "Có (≥2)",
"noneOrWheelchair": "Không/Giường/Xe lăn",
"crutchOrWalker": "Nạng/Khung",
"furniture": "Đồ nội thất",
"normalOrWheelchair": "Bình thường/Giường/Xe lăn",
"weak": "Yếu",
"impaired": "Suy giảm",
"correctSelfAssessment": "Đánh giá đúng",
"overestimate": "Đánh giá quá/Quên",
"assessmentRemark": "Ghi chú đánh giá",
"remarkPlaceholder": "Nhập ghi chú đánh giá",
"cancel": "Hủy",
"submitAssessment": "Gửi đánh giá",
"submitSuccess": "Gửi đánh giá thành công",
"submitFailed": "Gửi thất bại: {error}",
"exportDev": "Chức năng xuất đang phát triển"
},
"station": {
"inOut": "Quản lý NV/CT/Chuyển",
"inpatientRecord": "Bệnh án nội trú",
"orderProofread": "Đối chiếu y lệnh",
"orderExecution": "Thực hiện y lệnh",
"nursingRecord": "Hồ sơ điều dưỡng",
"tprSheet": "Phiếu nhiệt độ",
"inpatientBilling": "Viện phí nội trú",
"drugDistribution": "Lĩnh thuốc",
"consumableInventory": "Kiểm kê vật tư",
"rollFee": "Cấu hình phí tự động",
"medicineReturn": "Trả thuốc"
}
}
}

View File

@@ -1096,5 +1096,477 @@
"statusCompleted": "已完成",
"statusCancelled": "已取消",
"statusInProgress": "进行中"
},
"nursing": {
"assessment": {
"encounterId": "就诊号",
"assessmentType": "评估类型",
"assessmentTool": "评估工具",
"score": "评分",
"riskLevel": "风险等级",
"assessor": "评估人",
"assessmentTime": "评估时间",
"search": "搜索",
"painTitle": "疼痛评估NRS/VAS",
"patientId": "患者ID",
"patientName": "患者姓名",
"painScore": "疼痛评分",
"detail": "评估详情",
"assessorName": "评估人",
"submitAssessment": "提交评估",
"reset": "重置",
"assessmentRecords": "评估记录",
"detailPlaceholder": "请输入疼痛部位、性质、持续时间等",
"submitSuccess": "评估提交成功",
"submitFailed": "提交失败",
"getRecordsFailed": "获取记录失败",
"nutritionTitle": "营养风险筛查NRS2002",
"totalScore": "总评分",
"submitScreening": "提交筛查",
"screeningRecords": "筛查记录",
"screeningSubmitSuccess": "筛查提交成功",
"pipeTitle": "管道滑脱风险评估",
"interventionContent": "干预内容",
"nurseName": "护士姓名",
"interventionPlaceholder": "请输入评估内容,如:高风险、中风险、低风险、活动、固定等关键词",
"nursePlaceholder": "请输入护士姓名",
"createTime": "创建时间",
"valEncounterId": "请输入就诊号",
"valPatientId": "请输入患者ID",
"valPatientName": "请输入患者姓名",
"valPainScore": "请输入疼痛评分",
"valAssessor": "请输入评估人",
"valTotalScore": "请输入总评分",
"valInterventionContent": "请输入评估内容",
"valNurseName": "请输入护士姓名",
"valTemperature": "请输入体温",
"valPulse": "请输入脉搏",
"valSystolicBp": "请输入收缩压",
"valDiastolicBp": "请输入舒张压",
"valDrugName": "请输入药品名称",
"valBarcode": "请输入条码"
},
"mobile": {
"patientList": "患者列表",
"searchPlaceholder": "搜索姓名/床号",
"bedNo": "床号",
"nursingLevel": "护理等级",
"condition": "病情",
"diagnosis": "诊断",
"noPatients": "暂无患者",
"vitalSignEntry": "生命体征录入",
"patient": "患者",
"recordDate": "记录时间",
"selectDate": "选择日期",
"recordHour": "时点",
"temperature": "体温",
"pulse": "脉搏",
"respiration": "呼吸",
"systolicBp": "收缩压",
"diastolicBp": "舒张压",
"painScore": "疼痛评分",
"consciousness": "意识",
"consciousClear": "清醒",
"consciousDrowsy": "嗜睡",
"consciousConfused": "模糊",
"consciousStupor": "昏睡",
"consciousComaShallow": "浅昏迷",
"consciousComaDeep": "深昏迷",
"inputVolume": "入量",
"outputVolume": "出量",
"save": "保存",
"saveSuccess": "保存成功",
"saveFailed": "保存失败",
"unitTimesPerMin": "次/分",
"nursingAssessment": "护理评估",
"bradenPressure": "Braden压疮",
"morseFall": "Morse跌倒",
"nrs2002Nutrition": "NRS2002营养",
"totalScoreLabel": "总分",
"riskLevelLabel": "风险等级",
"assessmentRemark": "评估备注",
"optionalRemark": "可选备注",
"submitAssessmentBtn": "提交评估",
"assessmentRecordsLabel": "评估记录",
"noAssessmentRecords": "暂无评估记录",
"scoreLabel": "评分",
"completeAssessment": "请完成所有评估项目",
"assessmentSubmitSuccess": "评估提交成功",
"assessmentSubmitFailed": "提交失败",
"highRisk": "高风险",
"mediumRisk": "中风险",
"lowRisk": "低风险",
"normal": "正常",
"unknown": "未知",
"bradenSensory": "感觉感知",
"bradenMoisture": "潮湿程度",
"bradenActivity": "活动能力",
"bradenMobility": "移动能力",
"bradenNutrition": "营养摄取",
"bradenFriction": "摩擦力和剪切力",
"sensoryComplete": "完全受限",
"sensorySevere": "严重受限",
"sensoryMild": "轻度受限",
"sensoryNone": "未受损",
"moisturePersistent": "持续潮湿",
"moistureFrequent": "经常潮湿",
"moistureOccasional": "有时潮湿",
"moistureRare": "很少潮湿",
"activityBed": "卧床不起",
"activityChair": "仅限于椅",
"activityOccasionalWalk": "偶尔步行",
"activityFrequentWalk": "经常步行",
"mobilityNone": "完全不动",
"mobilitySevere": "严重受限",
"mobilityMild": "轻度受限",
"mobilityUnlimited": "不受限",
"nutritionVeryPoor": "非常差",
"nutritionProbablyInsufficient": "可能不足",
"nutritionAdequate": "充足",
"nutritionRich": "丰富",
"frictionProblem": "存在问题",
"frictionPotential": "有潜在问题",
"frictionNone": "无明显问题",
"morseFallHistory": "跌倒史",
"morseDiagnosis": "超过一个医学诊断",
"morseAmbulation": "行走辅助",
"morseIV": "静脉输液",
"morseGait": "步态",
"morseMental": "精神状态",
"yes": "有",
"no": "无",
"ambulationBedWheelchair": "卧床/轮椅",
"ambulationCrutch": "拐杖/助行器",
"ambulationFurniture": "扶家具",
"gaitNormal": "正常/卧床/轮椅",
"gaitWeak": "虚弱",
"gaitImpaired": "受损",
"mentalCorrect": "正确评估自身能力",
"mentalOverestimate": "高估或忘记限制",
"nrs2002BMI": "BMI(kg/m²)",
"nrs2002WeightLoss": "体重下降",
"nrs2002Intake": "饮食摄入",
"nrs2002Illness": "疾病严重程度",
"intakeNone": "无",
"intakePoor": "差",
"intakeModerate": "中等",
"intakeGood": "好",
"illnessSurgery": "大手术/创伤",
"illnessFracture": "骨盆骨折",
"illnessAcute": "慢性病急性发作",
"illnessMild": "无/轻度",
"orderExecuting": "执行中",
"orderProofread": "已校对",
"all": "全部",
"scanExecute": "扫码执行",
"type": "类型",
"frequency": "频次",
"dosage": "剂量",
"orderDoctor": "开嘱医生",
"execute": "执行",
"noOrders": "暂无医嘱",
"scanExecuteTitle": "扫码执行",
"barcode": "条码",
"barcodePlaceholder": "请扫描或输入条码",
"confirmExecute": "确认执行",
"confirmExecuteOrder": "确认执行医嘱",
"executeSuccess": "执行成功",
"executeFailed": "执行失败",
"orderMatchFailed": "未找到匹配的医嘱",
"infusionManagement": "输液管理",
"startInfusion": "开始输液",
"infusionSpeed": "输液速度",
"totalVolume": "总量(ml)",
"patrolNurse": "巡视护士",
"executeNurse": "执行护士",
"confirmStart": "确认开始",
"infusionPatrol": "输液巡视",
"dripRateLabel": "滴速(滴/分)",
"patencyStatus": "通畅状态",
"adverseReaction": "不良反应",
"emptyLeaveBlank": "无则留空",
"patrolNurseLabel": "巡视护士",
"confirmRecord": "确认记录",
"infusionStarted": "输液已开始",
"operationFailed": "操作失败",
"patrolRecordSaved": "巡视记录已保存",
"recordFailed": "记录失败",
"patrolRecord": "巡视记录",
"noInfusionRecords": "暂无输液记录",
"startInfusionTitle": "开始输液",
"drugName": "药品名称",
"drugNamePlaceholder": "请输入药品名称",
"infusionSpeedPlaceholder": "如: 40滴/分",
"occluded": "堵塞",
"phlebitis": "静脉炎",
"vitalTrend": "体征趋势",
"days3": "3天",
"days7": "7天",
"days14": "14天",
"temperatureChart": "体温 (°C)",
"pulseChart": "脉搏 (次/分)",
"bpChart": "血压 (mmHg)",
"respirationChart": "呼吸 (次/分)",
"noData": "暂无数据"
},
"quality": {
"title": "护理质量指标管理",
"refresh": "刷新",
"addIndicator": "新增指标",
"collectIndicator": "采集指标",
"exportReport": "导出报告",
"totalIndicators": "总指标",
"metCount": "达标数",
"unmetCount": "未达标数",
"meetRate": "达标率",
"deptCount": "科室数",
"avgMeetRate": "平均达标率",
"meetRateTrend": "达标率趋势",
"deptMeetCompare": "科室达标对比",
"categoryDistribution": "指标类别分布",
"unmetWarning": "未达标指标预警",
"indicatorName": "指标名称",
"department": "科室",
"targetValue": "目标值",
"actualValue": "实际值",
"gap": "差距",
"indicatorCategory": "指标类别",
"basicNursing": "基础护理",
"specializedNursing": "专科护理",
"nursingSafety": "护理安全",
"nursingDocumentation": "护理文书",
"sterilization": "消毒隔离",
"internalMedicine": "内科",
"surgery": "外科",
"obstetrics": "妇产科",
"pediatrics": "儿科",
"icu": "ICU",
"emergency": "急诊科",
"query": "查询",
"resetQuery": "重置",
"indicatorCode": "指标编码",
"category": "类别",
"metStatus": "达标状态",
"met": "达标",
"unmet": "未达标",
"statDate": "统计日期",
"remark": "备注",
"addDialog": "新增护理质量指标",
"indicatorCodePlaceholder": "如: NQ001",
"indicatorNamePlaceholder": "请输入指标名称",
"selectCategory": "请选择",
"departmentPlaceholder": "请输入科室",
"selectDate": "选择日期",
"cancel": "取消",
"confirm": "确定",
"addSuccess": "新增成功",
"exportDev": "导出功能开发中",
"collectSuccess": "已采集 {count} 项指标",
"collectFailed": "采集失败",
"monthLabels": ["1月", "2月", "3月", "4月", "5月", "6月"]
},
"execution": {
"title": "住院护士执行",
"scanTab": "执行扫码",
"addScan": "新增扫码",
"patient": "患者",
"type": "类型",
"order": "医嘱",
"barcode": "条码",
"result": "结果",
"success": "成功",
"failed": "失败",
"nurse": "护士",
"time": "时间",
"handoffTab": "交接班",
"addHandoff": "新增交接",
"keyPatientAlert": "重点患者提示",
"pending": "待办",
"ward": "病区",
"shift": "班次",
"handoffNurse": "交班人",
"receiveNurse": "接班人",
"patientCount": "患者",
"criticalCount": "危重",
"keyPatients": "重点患者",
"status": "状态",
"draft": "草稿",
"handedOff": "已交接",
"confirmed": "已确认",
"operation": "操作",
"confirm": "确认",
"confirmedMsg": "已确认",
"infusionTab": "输液巡视",
"addPatrol": "新增巡视",
"drug": "药品",
"dripRate": "滴速",
"patency": "通畅",
"patrolNurseLabel": "巡视护士",
"normal": "正常",
"abnormal": "异常"
},
"enhanced": {
"title": "护理评估增强",
"reminderTab": "评估提醒",
"carePlanTab": "护理计划",
"qualityTab": "质量指标",
"overdue": "逾期未评估",
"completedAssessment": "已完成评估",
"patient": "患者",
"assessmentType": "评估类型",
"frequencyHours": "频率(小时)",
"nextDeadline": "下次截止",
"status": "状态",
"pendingAssessment": "待评估",
"assessed": "已评估",
"overdueStatus": "已超时",
"completeAssessment": "完成评估",
"addCarePlan": "新增护理计划",
"nursingDiagnosis": "护理诊断",
"nursingGoal": "护理目标",
"nursingIntervention": "护理措施",
"newStatus": "新建",
"executing": "执行中",
"completed": "已完成",
"stopped": "已停止",
"evaluate": "评价",
"totalReminders": "总评估提醒",
"totalCompleted": "已完成",
"totalOverdue": "已超时",
"totalPlans": "总护理计划",
"completedPlans": "已完成计划",
"addCarePlanTitle": "新增护理计划",
"encounterId": "就诊ID",
"cancel": "取消",
"save": "保存",
"evaluateTitle": "评价护理计划",
"evaluatePrompt": "请输入评价结果",
"addSuccess": "新增成功",
"assessmentComplete": "评估完成",
"evaluateComplete": "评价完成",
"fallRisk": "跌倒风险",
"pressureUlcer": "压疮风险",
"nutrition": "营养筛查",
"pain": "疼痛评估",
"pipeline": "管道评估"
},
"enhancedAssessment": {
"title": "护理专项评估",
"refreshStats": "刷新统计",
"exportReport": "导出评估报告",
"totalAssessments": "总评估",
"highRiskPatients": "高危患者",
"mediumRiskPatients": "中危患者",
"intervened": "已干预",
"pendingAssessment": "待评估",
"assessmentRate": "评估率",
"bradenTab": "压疮评估(Braden)",
"newBraden": "新建Braden评估",
"morseTab": "跌倒评估(Morse)",
"newMorse": "新建Morse评估",
"nrs2002Tab": "营养筛查(NRS2002)",
"newNrs2002": "新建NRS2002评估",
"painTab": "疼痛评估",
"newPain": "新建疼痛评估",
"tubeTab": "管道风险评估",
"newTube": "新建管道评估",
"interventionTab": "护理措施",
"patient": "患者",
"totalScore": "总分",
"risk": "风险",
"assessor": "评估人",
"assessmentTime": "评估时间",
"remark": "备注",
"score": "评分",
"painLevel": "疼痛程度",
"interventionType": "措施类型",
"interventionContent": "措施内容",
"status": "状态",
"executor": "执行人",
"executeTime": "执行时间",
"high": "高危",
"medium": "中危",
"low": "低危",
"highRisk": "高风险",
"mediumRisk": "中风险",
"lowRisk": "低风险",
"severe": "重度",
"moderatePain": "中度",
"mildPain": "轻度",
"pendingExec": "待执行",
"patientName": "患者姓名",
"patientNamePlaceholder": "请输入患者姓名",
"encounterId": "就诊号",
"encounterIdPlaceholder": "请输入就诊号",
"bradenScale": "Braden压疮风险评估量表",
"sensation": "感觉",
"moisture": "潮湿",
"activity": "活动",
"mobility": "移动",
"nutrition": "营养",
"friction": "摩擦力",
"sensationComplete": "完全受限",
"sensationSevere": "严重受限",
"sensationMild": "轻度受限",
"sensationIntact": "未受损",
"moisturePersistent": "持续潮湿",
"moistureWet": "潮湿",
"moistureOccasional": "偶尔潮湿",
"moistureRare": "很少潮湿",
"activityBed": "卧床不起",
"activityWheelchair": "限于轮椅",
"activityOccasionalWalk": "偶尔步行",
"activityFrequentWalk": "经常步行",
"mobilityNone": "完全不能",
"mobilitySevere": "严重受限",
"mobilityMild": "轻度受限",
"mobilityUnlimited": "不受限",
"nutritionVeryPoor": "非常差",
"nutritionProbablyInsufficient": "可能不足",
"nutritionAdequate": "充足",
"nutritionRich": "丰富",
"frictionProblem": "存在问题",
"frictionPotential": "有潜在问题",
"frictionNone": "无明显问题",
"totalScoreLabel": "总分",
"morseScale": "Morse跌倒风险评估量表",
"fallHistory": "跌倒史",
"diagnosis": "诊断",
"ambulation": "行走辅助",
"ivTherapy": "静脉输液",
"gait": "步态",
"mentalState": "精神状态",
"no": "无",
"yes": "有",
"multiDiagnosis": "有(≥2个)",
"noneOrWheelchair": "无/卧床/轮椅",
"crutchOrWalker": "拐杖/助行器",
"furniture": "扶家具",
"normalOrWheelchair": "正常/卧床/轮椅",
"weak": "虚弱",
"impaired": "障碍",
"correctSelfAssessment": "正确评估自身能力",
"overestimate": "高估/忘记限制",
"assessmentRemark": "评估备注",
"remarkPlaceholder": "请输入评估备注",
"cancel": "取消",
"submitAssessment": "提交评估",
"submitSuccess": "评估提交成功",
"submitFailed": "提交失败: {error}",
"exportDev": "导出功能开发中"
},
"station": {
"inOut": "入出转管理",
"inpatientRecord": "住院病历",
"orderProofread": "医嘱校对",
"orderExecution": "医嘱执行",
"nursingRecord": "护理记录",
"tprSheet": "体温单",
"inpatientBilling": "住院记账",
"drugDistribution": "汇总领药",
"consumableInventory": "科室耗材盘点",
"rollFee": "滚费配置",
"medicineReturn": "退药管理"
}
}
}

View File

@@ -599,7 +599,7 @@ async function handleSubmit() {
}
} catch (error) {
console.error('补打挂号失败:', error);
proxy.$modal.msgError('补打挂号失败: ' + (error.message || '未知错误'));
proxy.$modal.msgError(t('billing.reprintFailMsg', { error: error.message || t('billing.reprintFail') }));
loading.value = false;
}
}
@@ -715,14 +715,14 @@ function printByHiprint() {
// 打印成功回调
hiprintTemplate.on('printSuccess', function (e) {
console.log('补打挂号单打印成功', e);
proxy.$modal.msgSuccess('打印成功');
proxy.$modal.msgSuccess(t('billing.printSuccess'));
dialogVisible.value = false;
});
// 打印失败回调
hiprintTemplate.on('printError', function (e) {
console.error('补打挂号单打印失败', e);
proxy.$modal.msgError('打印失败:' + (e.message || '未知错误'));
proxy.$modal.msgError(t('billing.printFailMsg', { error: e.message || t('billing.printFail') }));
});
// 执行打印
@@ -731,7 +731,7 @@ function printByHiprint() {
});
} catch (error) {
console.error('hiprint 打印挂号单失败:', error);
proxy.$modal.msgError('打印失败:' + (error.message || '未知错误'));
proxy.$modal.msgError(t('billing.printFailMsg', { error: error.message || t('billing.printFail') }));
}
}
@@ -743,7 +743,7 @@ function handleRecordSelect(row) {
// 确认选择记录
async function handleConfirmSelect() {
if (!selectedRecord.value) {
proxy.$modal.msgWarning('请选择一条挂号记录');
proxy.$modal.msgWarning(t('billing.selectRegRecord'));
return;
}
@@ -762,7 +762,7 @@ async function handleConfirmSelect() {
// 验证表单数据是否已正确填充
if (!form.encounterId) {
proxy.$modal.msgError('获取挂号记录ID失败请重试');
proxy.$modal.msgError(t('billing.getRegIdFail'));
loading.value = false;
return;
}
@@ -771,7 +771,7 @@ async function handleConfirmSelect() {
await handleSubmit();
} catch (error) {
console.error('确认选择记录失败:', error);
proxy.$modal.msgError('处理失败:' + (error.message || '未知错误'));
proxy.$modal.msgError(t('billing.processFail', { error: error.message || t('billing.reprintFail') }));
loading.value = false;
}
}

View File

@@ -1,109 +1,48 @@
<template>
<div class="inpatientNurse-home-container">
<el-container>
<!-- main -->
<el-container class="inpatientNurse-home-main">
<el-main>
<el-tabs
v-model="activeTabName"
type="card"
class="patient-tabs"
>
<el-tab-pane
label="入出转管理"
name="InOut"
>
<el-tabs v-model="activeTabName" type="card" class="patient-tabs">
<el-tab-pane :label="$t('nursing.station.inOut')" name="InOut">
<inOut v-if="activeTabName === 'InOut'" />
</el-tab-pane>
<el-tab-pane
label="住院病历"
name="InpatientRecord"
>
<el-tab-pane :label="$t('nursing.station.inpatientRecord')" name="InpatientRecord">
<el-container v-if="activeTabName === 'InpatientRecord'">
<PatientList
:selected-patient="patientInfo"
:on-select="handlePatientSelect"
/>
<PatientList :selected-patient="patientInfo" :on-select="handlePatientSelect" />
<el-container>
<el-header>
<inPatientBarDoctorFold />
</el-header>
<el-main>
<InpatientRecord :key="recordKey" />
</el-main>
<el-header><inPatientBarDoctorFold /></el-header>
<el-main><InpatientRecord :key="recordKey" /></el-main>
</el-container>
</el-container>
</el-tab-pane>
<el-tab-pane
label="医嘱校对"
name="MedicalOrderProofread"
>
<MedicalOrderProofread
v-if="activeTabName === 'MedicalOrderProofread'"
/>
<el-tab-pane :label="$t('nursing.station.orderProofread')" name="MedicalOrderProofread">
<MedicalOrderProofread v-if="activeTabName === 'MedicalOrderProofread'" />
</el-tab-pane>
<el-tab-pane
label="医嘱执行"
name="MedicalOrderExecution"
>
<MedicalOrderExecution
v-if="activeTabName === 'MedicalOrderExecution'"
/>
<el-tab-pane :label="$t('nursing.station.orderExecution')" name="MedicalOrderExecution">
<MedicalOrderExecution v-if="activeTabName === 'MedicalOrderExecution'" />
</el-tab-pane>
<el-tab-pane
label="护理记录"
name="NursingRecord"
>
<el-tab-pane :label="$t('nursing.station.nursingRecord')" name="NursingRecord">
<NursingRecord v-if="activeTabName === 'NursingRecord'" />
</el-tab-pane>
<el-tab-pane
label="体温单"
name="TprSheet"
>
<TprChart
v-if="activeTabName === 'TprSheet'"
:selected-patient="patientInfo"
:on-select="handlePatientSelect"
/>
<el-tab-pane :label="$t('nursing.station.tprSheet')" name="TprSheet">
<TprChart v-if="activeTabName === 'TprSheet'" :selected-patient="patientInfo" :on-select="handlePatientSelect" />
</el-tab-pane>
<el-tab-pane
label="住院记账"
name="InpatientBilling"
>
<el-tab-pane :label="$t('nursing.station.inpatientBilling')" name="InpatientBilling">
<InpatientBilling v-if="activeTabName === 'InpatientBilling'" />
</el-tab-pane>
<el-tab-pane
label="汇总领药"
name="DrugDistribution"
>
<el-tab-pane :label="$t('nursing.station.drugDistribution')" name="DrugDistribution">
<DrugDistribution v-if="activeTabName === 'DrugDistribution'" />
</el-tab-pane>
<el-tab-pane
label="科室耗材盘点"
name="ChkstockPartDeptDevice"
>
<ChkstockPartDeptDevice
v-if="activeTabName === 'ChkstockPartDeptDevice'"
/>
<el-tab-pane :label="$t('nursing.station.consumableInventory')" name="ChkstockPartDeptDevice">
<ChkstockPartDeptDevice v-if="activeTabName === 'ChkstockPartDeptDevice'" />
</el-tab-pane>
<el-tab-pane
label="滚费配置"
name="RollFee"
>
<el-tab-pane :label="$t('nursing.station.rollFee')" name="RollFee">
<RollFee v-if="activeTabName === 'RollFee'" />
</el-tab-pane>
<!-- <el-tab-pane label="皮试管理" name="I"> 皮试管理 </el-tab-pane>
<el-tab-pane label="出院管理" name="J">
<DischargedManagement></DischargedManagement>
</el-tab-pane> -->
<el-tab-pane
label="退药管理"
name="MedicineReturn"
>
<el-tab-pane :label="$t('nursing.station.medicineReturn')" name="MedicineReturn">
<MedicineReturn v-if="activeTabName === 'MedicineReturn'" />
</el-tab-pane>
<!-- <el-tab-pane label="手术记录" name="L"> 手术记录 </el-tab-pane> -->
</el-tabs>
</el-main>
</el-container>

View File

@@ -3,32 +3,32 @@
<el-card class="box-card">
<template #header>
<div class="card-header">
<span>营养风险筛查NRS2002</span>
<span>{{ $t('nursing.assessment.nutritionTitle') }}</span>
</div>
</template>
<el-form ref="formRef" :model="form" :rules="rules" label-width="120px">
<el-form-item label="就诊号" prop="encounterId">
<el-input v-model="form.encounterId" placeholder="请输入就诊号" />
<el-form-item :label="$t('nursing.assessment.encounterId')" prop="encounterId">
<el-input v-model="form.encounterId" :placeholder="$t('nursing.assessment.valEncounterId')" />
</el-form-item>
<el-form-item label="患者ID" prop="patientId">
<el-input v-model="form.patientId" placeholder="请输入患者ID" />
<el-form-item :label="$t('nursing.assessment.patientId')" prop="patientId">
<el-input v-model="form.patientId" :placeholder="$t('nursing.assessment.valPatientId')" />
</el-form-item>
<el-form-item label="患者姓名" prop="patientName">
<el-input v-model="form.patientName" placeholder="请输入患者姓名" />
<el-form-item :label="$t('nursing.assessment.patientName')" prop="patientName">
<el-input v-model="form.patientName" :placeholder="$t('nursing.assessment.valPatientName')" />
</el-form-item>
<el-form-item label="总评分" prop="totalScore">
<el-form-item :label="$t('nursing.assessment.totalScore')" prop="totalScore">
<el-input-number v-model="form.totalScore" :min="0" :max="10" />
</el-form-item>
<el-form-item label="评估详情" prop="detail">
<el-input v-model="form.detail" type="textarea" :rows="4" placeholder="请输入评估详情" />
<el-form-item :label="$t('nursing.assessment.detail')" prop="detail">
<el-input v-model="form.detail" type="textarea" :rows="4" :placeholder="$t('nursing.assessment.detailPlaceholder')" />
</el-form-item>
<el-form-item label="评估人" prop="assessorName">
<el-input v-model="form.assessorName" placeholder="请输入评估人姓名" />
<el-form-item :label="$t('nursing.assessment.assessorName')" prop="assessorName">
<el-input v-model="form.assessorName" :placeholder="$t('nursing.assessment.valAssessor')" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitScreen">提交筛查</el-button>
<el-button @click="resetForm">重置</el-button>
<el-button type="primary" @click="submitScreen">{{ $t('nursing.assessment.submitScreening') }}</el-button>
<el-button @click="resetForm">{{ $t('nursing.assessment.reset') }}</el-button>
</el-form-item>
</el-form>
</el-card>
@@ -36,32 +36,34 @@
<el-card class="box-card" style="margin-top: 20px;">
<template #header>
<div class="card-header">
<span>筛查记录</span>
<span>{{ $t('nursing.assessment.screeningRecords') }}</span>
</div>
</template>
<el-table v-loading="loading" :data="records" style="width: 100%">
<el-table-column label="总评分" prop="totalScore" width="80" />
<el-table-column label="风险等级" prop="riskLevel" width="100">
<el-table-column :label="$t('nursing.assessment.totalScore')" prop="totalScore" width="80" />
<el-table-column :label="$t('nursing.assessment.riskLevel')" prop="riskLevel" width="100">
<template #default="scope">
<el-tag :type="getRiskType(scope.row.riskLevel)">
{{ scope.row.riskLevel }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="评估详情" prop="detail" />
<el-table-column label="评估人" prop="assessorName" width="100" />
<el-table-column label="评估时间" prop="assessmentTime" width="170" />
<el-table-column :label="$t('nursing.assessment.detail')" prop="detail" />
<el-table-column :label="$t('nursing.assessment.assessorName')" prop="assessorName" width="100" />
<el-table-column :label="$t('nursing.assessment.assessmentTime')" prop="assessmentTime" width="170" />
</el-table>
</el-card>
</div>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue'
import { ref, reactive, computed, onMounted } from 'vue'
import { ElMessage } from 'element-plus'
import { useI18n } from 'vue-i18n'
import request from '@/utils/request'
const { t } = useI18n()
const formRef = ref(null)
const loading = ref(false)
const records = ref([])
@@ -75,13 +77,13 @@ const form = reactive({
assessorName: ''
})
const rules = {
encounterId: [{ required: true, message: '请输入就诊号', trigger: 'blur' }],
patientId: [{ required: true, message: '请输入患者ID', trigger: 'blur' }],
patientName: [{ required: true, message: '请输入患者姓名', trigger: 'blur' }],
totalScore: [{ required: true, message: '请输入总评分', trigger: 'blur' }],
assessorName: [{ required: true, message: '请输入评估人', trigger: 'blur' }]
}
const rules = computed(() => ({
encounterId: [{ required: true, message: t('nursing.assessment.valEncounterId'), trigger: 'blur' }],
patientId: [{ required: true, message: t('nursing.assessment.valPatientId'), trigger: 'blur' }],
patientName: [{ required: true, message: t('nursing.assessment.valPatientName'), trigger: 'blur' }],
totalScore: [{ required: true, message: t('nursing.assessment.valTotalScore'), trigger: 'blur' }],
assessorName: [{ required: true, message: t('nursing.assessment.valAssessor'), trigger: 'blur' }]
}))
const getRiskType = (level) => {
const map = { HIGH: 'danger', MEDIUM: 'warning', LOW: 'success', NORMAL: 'info' }
@@ -97,11 +99,11 @@ const submitScreen = async () => {
method: 'post',
data: form
})
ElMessage.success('筛查提交成功')
ElMessage.success(t('nursing.assessment.screeningSubmitSuccess'))
getRecords()
resetForm()
} catch (error) {
ElMessage.error('提交失败')
ElMessage.error(t('nursing.assessment.submitFailed'))
} finally {
loading.value = false
}
@@ -117,7 +119,7 @@ const getRecords = async () => {
})
records.value = res.data || []
} catch (error) {
ElMessage.error('获取记录失败')
ElMessage.error(t('nursing.assessment.getRecordsFailed'))
} finally {
loading.value = false
}
@@ -140,4 +142,4 @@ onMounted(() => {
justify-content: space-between;
align-items: center;
}
</style>
</style>

View File

@@ -3,32 +3,32 @@
<el-card class="box-card">
<template #header>
<div class="card-header">
<span>疼痛评估NRS/VAS</span>
<span>{{ $t('nursing.assessment.painTitle') }}</span>
</div>
</template>
<el-form ref="formRef" :model="form" :rules="rules" label-width="120px">
<el-form-item label="就诊号" prop="encounterId">
<el-input v-model="form.encounterId" placeholder="请输入就诊号" />
<el-form-item :label="$t('nursing.assessment.encounterId')" prop="encounterId">
<el-input v-model="form.encounterId" :placeholder="$t('nursing.assessment.valEncounterId')" />
</el-form-item>
<el-form-item label="患者ID" prop="patientId">
<el-input v-model="form.patientId" placeholder="请输入患者ID" />
<el-form-item :label="$t('nursing.assessment.patientId')" prop="patientId">
<el-input v-model="form.patientId" :placeholder="$t('nursing.assessment.valPatientId')" />
</el-form-item>
<el-form-item label="患者姓名" prop="patientName">
<el-input v-model="form.patientName" placeholder="请输入患者姓名" />
<el-form-item :label="$t('nursing.assessment.patientName')" prop="patientName">
<el-input v-model="form.patientName" :placeholder="$t('nursing.assessment.valPatientName')" />
</el-form-item>
<el-form-item label="疼痛评分" prop="totalScore">
<el-form-item :label="$t('nursing.assessment.painScore')" prop="totalScore">
<el-input-number v-model="form.totalScore" :min="0" :max="10" />
</el-form-item>
<el-form-item label="评估详情" prop="detail">
<el-input v-model="form.detail" type="textarea" :rows="4" placeholder="请输入疼痛部位、性质、持续时间等" />
<el-form-item :label="$t('nursing.assessment.detail')" prop="detail">
<el-input v-model="form.detail" type="textarea" :rows="4" :placeholder="$t('nursing.assessment.detailPlaceholder')" />
</el-form-item>
<el-form-item label="评估人" prop="assessorName">
<el-input v-model="form.assessorName" placeholder="请输入评估人姓名" />
<el-form-item :label="$t('nursing.assessment.assessorName')" prop="assessorName">
<el-input v-model="form.assessorName" :placeholder="$t('nursing.assessment.valAssessor')" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitAssess">提交评估</el-button>
<el-button @click="resetForm">重置</el-button>
<el-button type="primary" @click="submitAssess">{{ $t('nursing.assessment.submitAssessment') }}</el-button>
<el-button @click="resetForm">{{ $t('nursing.assessment.reset') }}</el-button>
</el-form-item>
</el-form>
</el-card>
@@ -36,32 +36,34 @@
<el-card class="box-card" style="margin-top: 20px;">
<template #header>
<div class="card-header">
<span>评估记录</span>
<span>{{ $t('nursing.assessment.assessmentRecords') }}</span>
</div>
</template>
<el-table v-loading="loading" :data="records" style="width: 100%">
<el-table-column label="疼痛评分" prop="totalScore" width="80" />
<el-table-column label="风险等级" prop="riskLevel" width="100">
<el-table-column :label="$t('nursing.assessment.painScore')" prop="totalScore" width="80" />
<el-table-column :label="$t('nursing.assessment.riskLevel')" prop="riskLevel" width="100">
<template #default="scope">
<el-tag :type="getRiskType(scope.row.riskLevel)">
{{ scope.row.riskLevel }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="评估详情" prop="detail" />
<el-table-column label="评估人" prop="assessorName" width="100" />
<el-table-column label="评估时间" prop="assessmentTime" width="170" />
<el-table-column :label="$t('nursing.assessment.detail')" prop="detail" />
<el-table-column :label="$t('nursing.assessment.assessorName')" prop="assessorName" width="100" />
<el-table-column :label="$t('nursing.assessment.assessmentTime')" prop="assessmentTime" width="170" />
</el-table>
</el-card>
</div>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue'
import { ref, reactive, computed, onMounted } from 'vue'
import { ElMessage } from 'element-plus'
import { useI18n } from 'vue-i18n'
import request from '@/utils/request'
const { t } = useI18n()
const formRef = ref(null)
const loading = ref(false)
const records = ref([])
@@ -75,13 +77,13 @@ const form = reactive({
assessorName: ''
})
const rules = {
encounterId: [{ required: true, message: '请输入就诊号', trigger: 'blur' }],
patientId: [{ required: true, message: '请输入患者ID', trigger: 'blur' }],
patientName: [{ required: true, message: '请输入患者姓名', trigger: 'blur' }],
totalScore: [{ required: true, message: '请输入疼痛评分', trigger: 'blur' }],
assessorName: [{ required: true, message: '请输入评估人', trigger: 'blur' }]
}
const rules = computed(() => ({
encounterId: [{ required: true, message: t('nursing.assessment.valEncounterId'), trigger: 'blur' }],
patientId: [{ required: true, message: t('nursing.assessment.valPatientId'), trigger: 'blur' }],
patientName: [{ required: true, message: t('nursing.assessment.valPatientName'), trigger: 'blur' }],
totalScore: [{ required: true, message: t('nursing.assessment.valPainScore'), trigger: 'blur' }],
assessorName: [{ required: true, message: t('nursing.assessment.valAssessor'), trigger: 'blur' }]
}))
const getRiskType = (level) => {
const map = { HIGH: 'danger', MEDIUM: 'warning', LOW: 'success', NORMAL: 'info' }
@@ -97,11 +99,11 @@ const submitAssess = async () => {
method: 'post',
data: form
})
ElMessage.success('评估提交成功')
ElMessage.success(t('nursing.assessment.submitSuccess'))
getRecords()
resetForm()
} catch (error) {
ElMessage.error('提交失败')
ElMessage.error(t('nursing.assessment.submitFailed'))
} finally {
loading.value = false
}
@@ -117,7 +119,7 @@ const getRecords = async () => {
})
records.value = res.data || []
} catch (error) {
ElMessage.error('获取记录失败')
ElMessage.error(t('nursing.assessment.getRecordsFailed'))
} finally {
loading.value = false
}
@@ -140,4 +142,4 @@ onMounted(() => {
justify-content: space-between;
align-items: center;
}
</style>
</style>

View File

@@ -3,26 +3,26 @@
<el-card class="box-card">
<template #header>
<div class="card-header">
<span>管道滑脱风险评估</span>
<span>{{ $t('nursing.assessment.pipeTitle') }}</span>
</div>
</template>
<el-form ref="formRef" :model="form" :rules="rules" label-width="120px">
<el-form-item label="就诊号" prop="encounterId">
<el-input v-model="form.encounterId" placeholder="请输入就诊号" />
<el-form-item :label="$t('nursing.assessment.encounterId')" prop="encounterId">
<el-input v-model="form.encounterId" :placeholder="$t('nursing.assessment.valEncounterId')" />
</el-form-item>
<el-form-item label="患者ID" prop="patientId">
<el-input v-model="form.patientId" placeholder="请输入患者ID" />
<el-form-item :label="$t('nursing.assessment.patientId')" prop="patientId">
<el-input v-model="form.patientId" :placeholder="$t('nursing.assessment.valPatientId')" />
</el-form-item>
<el-form-item label="干预内容" prop="interventionContent">
<el-input v-model="form.interventionContent" type="textarea" :rows="4" placeholder="请输入评估内容,如:高风险、中风险、低风险、活动、固定等关键词" />
<el-form-item :label="$t('nursing.assessment.interventionContent')" prop="interventionContent">
<el-input v-model="form.interventionContent" type="textarea" :rows="4" :placeholder="$t('nursing.assessment.interventionPlaceholder')" />
</el-form-item>
<el-form-item label="护士姓名" prop="nurseName">
<el-input v-model="form.nurseName" placeholder="请输入护士姓名" />
<el-form-item :label="$t('nursing.assessment.nurseName')" prop="nurseName">
<el-input v-model="form.nurseName" :placeholder="$t('nursing.assessment.nursePlaceholder')" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitAssess">提交评估</el-button>
<el-button @click="resetForm">重置</el-button>
<el-button type="primary" @click="submitAssess">{{ $t('nursing.assessment.submitAssessment') }}</el-button>
<el-button @click="resetForm">{{ $t('nursing.assessment.reset') }}</el-button>
</el-form-item>
</el-form>
</el-card>
@@ -30,38 +30,40 @@
<el-card class="box-card" style="margin-top: 20px;">
<template #header>
<div class="card-header">
<span>评估记录</span>
<span>{{ $t('nursing.assessment.assessmentRecords') }}</span>
</div>
</template>
<el-table v-loading="loading" :data="records" style="width: 100%">
<el-table-column label="风险等级" prop="riskLevel" width="100">
<el-table-column :label="$t('nursing.assessment.riskLevel')" prop="riskLevel" width="100">
<template #default="scope">
<el-tag :type="getRiskType(scope.row.riskLevel)">
{{ scope.row.riskLevel }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="干预内容" prop="interventionContent" />
<el-table-column label="护士" prop="nurseName" width="100" />
<el-table-column label="状态" prop="status" width="100">
<el-table-column :label="$t('nursing.assessment.interventionContent')" prop="interventionContent" />
<el-table-column :label="$t('nursing.assessment.nurseName')" prop="nurseName" width="100" />
<el-table-column :label="$t('common.status')" prop="status" width="100">
<template #default="scope">
<el-tag :type="getStatusType(scope.row.status)">
{{ scope.row.status }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="创建时间" prop="createTime" width="170" />
<el-table-column :label="$t('nursing.assessment.createTime')" prop="createTime" width="170" />
</el-table>
</el-card>
</div>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue'
import { ref, reactive, computed, onMounted } from 'vue'
import { ElMessage } from 'element-plus'
import { useI18n } from 'vue-i18n'
import request from '@/utils/request'
const { t } = useI18n()
const formRef = ref(null)
const loading = ref(false)
const records = ref([])
@@ -73,12 +75,12 @@ const form = reactive({
nurseName: ''
})
const rules = {
encounterId: [{ required: true, message: '请输入就诊号', trigger: 'blur' }],
patientId: [{ required: true, message: '请输入患者ID', trigger: 'blur' }],
interventionContent: [{ required: true, message: '请输入评估内容', trigger: 'blur' }],
nurseName: [{ required: true, message: '请输入护士姓名', trigger: 'blur' }]
}
const rules = computed(() => ({
encounterId: [{ required: true, message: t('nursing.assessment.valEncounterId'), trigger: 'blur' }],
patientId: [{ required: true, message: t('nursing.assessment.valPatientId'), trigger: 'blur' }],
interventionContent: [{ required: true, message: t('nursing.assessment.valInterventionContent'), trigger: 'blur' }],
nurseName: [{ required: true, message: t('nursing.assessment.valNurseName'), trigger: 'blur' }]
}))
const getRiskType = (level) => {
const map = { HIGH: 'danger', MEDIUM: 'warning', LOW: 'success' }
@@ -99,11 +101,11 @@ const submitAssess = async () => {
method: 'post',
data: form
})
ElMessage.success('评估提交成功')
ElMessage.success(t('nursing.assessment.submitSuccess'))
getRecords()
resetForm()
} catch (error) {
ElMessage.error('提交失败')
ElMessage.error(t('nursing.assessment.submitFailed'))
} finally {
loading.value = false
}
@@ -119,7 +121,7 @@ const getRecords = async () => {
})
records.value = res.data || []
} catch (error) {
ElMessage.error('获取记录失败')
ElMessage.error(t('nursing.assessment.getRecordsFailed'))
} finally {
loading.value = false
}
@@ -142,4 +144,4 @@ onMounted(() => {
justify-content: space-between;
align-items: center;
}
</style>
</style>

View File

@@ -4,7 +4,7 @@
:model="q"
:inline="true"
>
<el-form-item label="就诊号">
<el-form-item :label="$t('nursing.assessment.encounterId')">
<el-input
v-model="q.encounterId"
clearable
@@ -15,7 +15,7 @@
type="primary"
@click="getList"
>
搜索
{{ $t('nursing.assessment.search') }}
</el-button>
</el-form-item>
</el-form>
@@ -24,22 +24,22 @@
:data="list"
>
<el-table-column
label="评估类型"
:label="$t('nursing.assessment.assessmentType')"
prop="assessmentType"
width="100"
/>
<el-table-column
label="评估工具"
:label="$t('nursing.assessment.assessmentTool')"
prop="assessmentTool"
width="120"
/>
<el-table-column
label="评分"
:label="$t('nursing.assessment.score')"
prop="totalScore"
width="80"
/>
<el-table-column
label="风险等级"
:label="$t('nursing.assessment.riskLevel')"
prop="riskLevel"
width="100"
>
@@ -50,12 +50,12 @@
</template>
</el-table-column>
<el-table-column
label="评估人"
:label="$t('nursing.assessment.assessor')"
prop="assessorName"
width="100"
/>
<el-table-column
label="评估时间"
:label="$t('nursing.assessment.assessmentTime')"
prop="assessmentTime"
width="170"
/>

View File

@@ -1,613 +1,258 @@
<template>
<div style="padding:16px">
<div style="margin-bottom:16px;display:flex;justify-content:space-between;align-items:center">
<span style="font-size:18px;font-weight:bold">护理专项评估</span>
<span style="font-size:18px;font-weight:bold">{{ $t('nursing.enhancedAssessment.title') }}</span>
<div>
<el-button
type="primary"
@click="loadStats"
>
刷新统计
</el-button>
<el-button
type="success"
@click="exportReport"
>
导出评估报告
</el-button>
<el-button type="primary" @click="loadStats">{{ $t('nursing.enhancedAssessment.refreshStats') }}</el-button>
<el-button type="success" @click="exportReport">{{ $t('nursing.enhancedAssessment.exportReport') }}</el-button>
</div>
</div>
<!-- 统计卡片 -->
<el-row
:gutter="12"
style="margin-bottom:16px"
>
<el-col
v-for="item in statCards"
:key="item.label"
:span="4"
>
<el-card
shadow="hover"
:body-style="{padding:'12px'}"
>
<el-row :gutter="12" style="margin-bottom:16px">
<el-col v-for="item in statCardsComputed" :key="item.label" :span="4">
<el-card shadow="hover" :body-style="{padding:'12px'}">
<div style="text-align:center">
<div
style="font-size:20px;font-weight:bold"
:style="{color:item.color}"
>
{{ item.value }}
</div>
<div style="font-size:12px;color:#999">
{{ item.label }}
</div>
<div style="font-size:20px;font-weight:bold" :style="{color:item.color}">{{ item.value }}</div>
<div style="font-size:12px;color:#999">{{ item.label }}</div>
</div>
</el-card>
</el-col>
</el-row>
<!-- 评估类型Tab -->
<el-tabs
v-model="activeTab"
type="border-card"
>
<!-- 压疮评估(Braden) -->
<el-tab-pane
label="压疮评估(Braden)"
name="BRADEN"
>
<el-tabs v-model="activeTab" type="border-card">
<el-tab-pane :label="$t('nursing.enhancedAssessment.bradenTab')" name="BRADEN">
<div style="margin-bottom:12px">
<el-button
type="success"
@click="openAssess('BRADEN')"
>
新建Braden评估
</el-button>
<el-button type="success" @click="openAssess('BRADEN')">{{ $t('nursing.enhancedAssessment.newBraden') }}</el-button>
</div>
<el-table
:data="bradenData"
border
stripe
>
<el-table-column
prop="patientName"
label="患者"
width="100"
/>
<el-table-column
prop="totalScore"
label="总分"
width="70"
align="center"
/>
<el-table-column
prop="riskLevel"
label="风险"
width="80"
>
<el-table :data="bradenData" border stripe>
<el-table-column prop="patientName" :label="$t('nursing.enhancedAssessment.patient')" width="100" />
<el-table-column prop="totalScore" :label="$t('nursing.enhancedAssessment.totalScore')" width="70" align="center" />
<el-table-column prop="riskLevel" :label="$t('nursing.enhancedAssessment.risk')" width="80">
<template #default="{row}">
<el-tag
:type="row.riskLevel==='HIGH'?'danger':row.riskLevel==='MEDIUM'?'warning':'success'"
size="small"
>
{{ row.riskLevel==='HIGH'?'高危':row.riskLevel==='MEDIUM'?'中危':row.riskLevel==='LOW'?'低危':'正常' }}
<el-tag :type="row.riskLevel==='HIGH'?'danger':row.riskLevel==='MEDIUM'?'warning':'success'" size="small">
{{ row.riskLevel==='HIGH' ? $t('nursing.enhancedAssessment.high') : row.riskLevel==='MEDIUM' ? $t('nursing.enhancedAssessment.medium') : row.riskLevel==='LOW' ? $t('nursing.enhancedAssessment.low') : $t('nursing.mobile.normal') }}
</el-tag>
</template>
</el-table-column>
<el-table-column
prop="assessorName"
label="评估人"
width="80"
/>
<el-table-column
prop="assessmentTime"
label="评估时间"
width="170"
/>
<el-table-column
prop="detail"
label="备注"
show-overflow-tooltip
/>
<el-table-column prop="assessorName" :label="$t('nursing.enhancedAssessment.assessor')" width="80" />
<el-table-column prop="assessmentTime" :label="$t('nursing.enhancedAssessment.assessmentTime')" width="170" />
<el-table-column prop="detail" :label="$t('nursing.enhancedAssessment.remark')" show-overflow-tooltip />
</el-table>
</el-tab-pane>
<!-- 跌倒评估(Morse) -->
<el-tab-pane
label="跌倒评估(Morse)"
name="MORSE"
>
<el-tab-pane :label="$t('nursing.enhancedAssessment.morseTab')" name="MORSE">
<div style="margin-bottom:12px">
<el-button
type="success"
@click="openAssess('MORSE')"
>
新建Morse评估
</el-button>
<el-button type="success" @click="openAssess('MORSE')">{{ $t('nursing.enhancedAssessment.newMorse') }}</el-button>
</div>
<el-table
:data="morseData"
border
stripe
>
<el-table-column
prop="patientName"
label="患者"
width="100"
/>
<el-table-column
prop="totalScore"
label="总分"
width="70"
align="center"
/>
<el-table-column
prop="riskLevel"
label="风险"
width="80"
>
<el-table :data="morseData" border stripe>
<el-table-column prop="patientName" :label="$t('nursing.enhancedAssessment.patient')" width="100" />
<el-table-column prop="totalScore" :label="$t('nursing.enhancedAssessment.totalScore')" width="70" align="center" />
<el-table-column prop="riskLevel" :label="$t('nursing.enhancedAssessment.risk')" width="80">
<template #default="{row}">
<el-tag
:type="row.riskLevel==='HIGH'?'danger':row.riskLevel==='MEDIUM'?'warning':'success'"
size="small"
>
{{ row.riskLevel==='HIGH'?'高危':row.riskLevel==='MEDIUM'?'中危':row.riskLevel==='LOW'?'低危':'正常' }}
<el-tag :type="row.riskLevel==='HIGH'?'danger':row.riskLevel==='MEDIUM'?'warning':'success'" size="small">
{{ row.riskLevel==='HIGH' ? $t('nursing.enhancedAssessment.high') : row.riskLevel==='MEDIUM' ? $t('nursing.enhancedAssessment.medium') : row.riskLevel==='LOW' ? $t('nursing.enhancedAssessment.low') : $t('nursing.mobile.normal') }}
</el-tag>
</template>
</el-table-column>
<el-table-column
prop="assessorName"
label="评估人"
width="80"
/>
<el-table-column
prop="assessmentTime"
label="评估时间"
width="170"
/>
<el-table-column prop="assessorName" :label="$t('nursing.enhancedAssessment.assessor')" width="80" />
<el-table-column prop="assessmentTime" :label="$t('nursing.enhancedAssessment.assessmentTime')" width="170" />
</el-table>
</el-tab-pane>
<!-- 营养筛查(NRS2002) -->
<el-tab-pane
label="营养筛查(NRS2002)"
name="NRS2002"
>
<el-tab-pane :label="$t('nursing.enhancedAssessment.nrs2002Tab')" name="NRS2002">
<div style="margin-bottom:12px">
<el-button
type="success"
@click="openAssess('NRS2002')"
>
新建NRS2002评估
</el-button>
<el-button type="success" @click="openAssess('NRS2002')">{{ $t('nursing.enhancedAssessment.newNrs2002') }}</el-button>
</div>
<el-table
:data="nrsData"
border
stripe
>
<el-table-column
prop="patientName"
label="患者"
width="100"
/>
<el-table-column
prop="totalScore"
label="总分"
width="70"
align="center"
/>
<el-table-column
prop="riskLevel"
label="风险"
width="80"
>
<el-table :data="nrsData" border stripe>
<el-table-column prop="patientName" :label="$t('nursing.enhancedAssessment.patient')" width="100" />
<el-table-column prop="totalScore" :label="$t('nursing.enhancedAssessment.totalScore')" width="70" align="center" />
<el-table-column prop="riskLevel" :label="$t('nursing.enhancedAssessment.risk')" width="80">
<template #default="{row}">
<el-tag
:type="row.riskLevel==='HIGH'?'danger':row.riskLevel==='MEDIUM'?'warning':'success'"
size="small"
>
{{ row.riskLevel==='HIGH'?'高风险':row.riskLevel==='MEDIUM'?'中风险':'低风险' }}
<el-tag :type="row.riskLevel==='HIGH'?'danger':row.riskLevel==='MEDIUM'?'warning':'success'" size="small">
{{ row.riskLevel==='HIGH' ? $t('nursing.enhancedAssessment.highRisk') : row.riskLevel==='MEDIUM' ? $t('nursing.enhancedAssessment.mediumRisk') : $t('nursing.enhancedAssessment.lowRisk') }}
</el-tag>
</template>
</el-table-column>
<el-table-column
prop="assessorName"
label="评估人"
width="80"
/>
<el-table-column
prop="assessmentTime"
label="评估时间"
width="170"
/>
<el-table-column prop="assessorName" :label="$t('nursing.enhancedAssessment.assessor')" width="80" />
<el-table-column prop="assessmentTime" :label="$t('nursing.enhancedAssessment.assessmentTime')" width="170" />
</el-table>
</el-tab-pane>
<!-- 疼痛评估 -->
<el-tab-pane
label="疼痛评估"
name="PAIN"
>
<el-tab-pane :label="$t('nursing.enhancedAssessment.painTab')" name="PAIN">
<div style="margin-bottom:12px">
<el-button
type="success"
@click="openAssess('PAIN')"
>
新建疼痛评估
</el-button>
<el-button type="success" @click="openAssess('PAIN')">{{ $t('nursing.enhancedAssessment.newPain') }}</el-button>
</div>
<el-table
:data="painData"
border
stripe
>
<el-table-column
prop="patientName"
label="患者"
width="100"
/>
<el-table-column
prop="totalScore"
label="评分"
width="70"
align="center"
/>
<el-table-column
prop="riskLevel"
label="疼痛程度"
width="100"
>
<el-table :data="painData" border stripe>
<el-table-column prop="patientName" :label="$t('nursing.enhancedAssessment.patient')" width="100" />
<el-table-column prop="totalScore" :label="$t('nursing.enhancedAssessment.score')" width="70" align="center" />
<el-table-column prop="riskLevel" :label="$t('nursing.enhancedAssessment.painLevel')" width="100">
<template #default="{row}">
<el-tag
:type="row.riskLevel==='HIGH'?'danger':row.riskLevel==='MEDIUM'?'warning':'success'"
size="small"
>
{{ row.riskLevel==='HIGH'?'重度':row.riskLevel==='MEDIUM'?'中度':'轻度' }}
<el-tag :type="row.riskLevel==='HIGH'?'danger':row.riskLevel==='MEDIUM'?'warning':'success'" size="small">
{{ row.riskLevel==='HIGH' ? $t('nursing.enhancedAssessment.severe') : row.riskLevel==='MEDIUM' ? $t('nursing.enhancedAssessment.moderatePain') : $t('nursing.enhancedAssessment.mildPain') }}
</el-tag>
</template>
</el-table-column>
<el-table-column
prop="assessorName"
label="评估人"
width="80"
/>
<el-table-column
prop="assessmentTime"
label="评估时间"
width="170"
/>
<el-table-column prop="assessorName" :label="$t('nursing.enhancedAssessment.assessor')" width="80" />
<el-table-column prop="assessmentTime" :label="$t('nursing.enhancedAssessment.assessmentTime')" width="170" />
</el-table>
</el-tab-pane>
<!-- 管道风险评估 -->
<el-tab-pane
label="管道风险评估"
name="TUBE"
>
<el-tab-pane :label="$t('nursing.enhancedAssessment.tubeTab')" name="TUBE">
<div style="margin-bottom:12px">
<el-button
type="success"
@click="openAssess('TUBE')"
>
新建管道评估
</el-button>
<el-button type="success" @click="openAssess('TUBE')">{{ $t('nursing.enhancedAssessment.newTube') }}</el-button>
</div>
<el-table
:data="tubeData"
border
stripe
>
<el-table-column
prop="patientName"
label="患者"
width="100"
/>
<el-table-column
prop="totalScore"
label="评分"
width="70"
align="center"
/>
<el-table-column
prop="riskLevel"
label="风险"
width="80"
>
<el-table :data="tubeData" border stripe>
<el-table-column prop="patientName" :label="$t('nursing.enhancedAssessment.patient')" width="100" />
<el-table-column prop="totalScore" :label="$t('nursing.enhancedAssessment.score')" width="70" align="center" />
<el-table-column prop="riskLevel" :label="$t('nursing.enhancedAssessment.risk')" width="80">
<template #default="{row}">
<el-tag
:type="row.riskLevel==='HIGH'?'danger':row.riskLevel==='MEDIUM'?'warning':'success'"
size="small"
>
{{ row.riskLevel==='HIGH'?'高危':row.riskLevel==='MEDIUM'?'中危':'低危' }}
<el-tag :type="row.riskLevel==='HIGH'?'danger':row.riskLevel==='MEDIUM'?'warning':'success'" size="small">
{{ row.riskLevel==='HIGH' ? $t('nursing.enhancedAssessment.high') : row.riskLevel==='MEDIUM' ? $t('nursing.enhancedAssessment.medium') : $t('nursing.enhancedAssessment.low') }}
</el-tag>
</template>
</el-table-column>
<el-table-column
prop="assessorName"
label="评估人"
width="80"
/>
<el-table-column
prop="assessmentTime"
label="评估时间"
width="170"
/>
<el-table-column prop="assessorName" :label="$t('nursing.enhancedAssessment.assessor')" width="80" />
<el-table-column prop="assessmentTime" :label="$t('nursing.enhancedAssessment.assessmentTime')" width="170" />
</el-table>
</el-tab-pane>
<!-- 护理措施 -->
<el-tab-pane
label="护理措施"
name="INTERVENTION"
>
<el-table
:data="interventionData"
border
stripe
>
<el-table-column
prop="patientName"
label="患者"
width="100"
/>
<el-table-column
prop="interventionType"
label="措施类型"
width="120"
/>
<el-table-column
prop="interventionContent"
label="措施内容"
min-width="200"
show-overflow-tooltip
/>
<el-table-column
prop="status"
label="状态"
width="80"
>
<el-tab-pane :label="$t('nursing.enhancedAssessment.interventionTab')" name="INTERVENTION">
<el-table :data="interventionData" border stripe>
<el-table-column prop="patientName" :label="$t('nursing.enhancedAssessment.patient')" width="100" />
<el-table-column prop="interventionType" :label="$t('nursing.enhancedAssessment.interventionType')" width="120" />
<el-table-column prop="interventionContent" :label="$t('nursing.enhancedAssessment.interventionContent')" min-width="200" show-overflow-tooltip />
<el-table-column prop="status" :label="$t('nursing.enhancedAssessment.status')" width="80">
<template #default="{row}">
<el-tag
:type="row.status==='COMPLETED'?'success':row.status==='EXECUTING'?'warning':'info'"
size="small"
>
{{ row.status==='COMPLETED'?'已完成':row.status==='EXECUTING'?'执行中':'待执行' }}
<el-tag :type="row.status==='COMPLETED'?'success':row.status==='EXECUTING'?'warning':'info'" size="small">
{{ row.status==='COMPLETED' ? $t('nursing.enhanced.completed') : row.status==='EXECUTING' ? $t('nursing.enhanced.executing') : $t('nursing.enhancedAssessment.pendingExec') }}
</el-tag>
</template>
</el-table-column>
<el-table-column
prop="executorName"
label="执行人"
width="80"
/>
<el-table-column
prop="executeTime"
label="执行时间"
width="170"
/>
<el-table-column prop="executorName" :label="$t('nursing.enhancedAssessment.executor')" width="80" />
<el-table-column prop="executeTime" :label="$t('nursing.enhancedAssessment.executeTime')" width="170" />
</el-table>
</el-tab-pane>
</el-tabs>
<!-- 评估弹窗 -->
<el-dialog
v-model="assessVisible"
:title="assessDialogTitle"
width="700px"
append-to-body
>
<el-form
:model="assessForm"
label-width="120px"
>
<el-form-item label="患者姓名">
<el-input
v-model="assessForm.patientName"
placeholder="请输入患者姓名"
style="width:200px"
/>
<el-dialog v-model="assessVisible" :title="assessDialogTitle" width="700px" append-to-body>
<el-form :model="assessForm" label-width="120px">
<el-form-item :label="$t('nursing.enhancedAssessment.patientName')">
<el-input v-model="assessForm.patientName" :placeholder="$t('nursing.enhancedAssessment.patientNamePlaceholder')" style="width:200px" />
</el-form-item>
<el-form-item label="就诊号">
<el-input
v-model="assessForm.encounterId"
placeholder="请输入就诊号"
style="width:200px"
/>
<el-form-item :label="$t('nursing.enhancedAssessment.encounterId')">
<el-input v-model="assessForm.encounterId" :placeholder="$t('nursing.enhancedAssessment.encounterIdPlaceholder')" style="width:200px" />
</el-form-item>
<!-- Braden量表 -->
<template v-if="assessForm.assessmentTool==='BRADEN'">
<el-divider content-position="left">
Braden压疮风险评估量表
</el-divider>
<el-form-item label="感觉">
<el-divider content-position="left">{{ $t('nursing.enhancedAssessment.bradenScale') }}</el-divider>
<el-form-item :label="$t('nursing.enhancedAssessment.sensation')">
<el-radio-group v-model="bradenScores.sensation">
<el-radio :value="1">
完全受限
</el-radio><el-radio :value="2">
严重受限
</el-radio>
<el-radio :value="3">
轻度受限
</el-radio><el-radio :value="4">
未受损
</el-radio>
<el-radio :value="1">{{ $t('nursing.enhancedAssessment.sensationComplete') }}</el-radio>
<el-radio :value="2">{{ $t('nursing.enhancedAssessment.sensationSevere') }}</el-radio>
<el-radio :value="3">{{ $t('nursing.enhancedAssessment.sensationMild') }}</el-radio>
<el-radio :value="4">{{ $t('nursing.enhancedAssessment.sensationIntact') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="潮湿">
<el-form-item :label="$t('nursing.enhancedAssessment.moisture')">
<el-radio-group v-model="bradenScores.moisture">
<el-radio :value="1">
持续潮湿
</el-radio><el-radio :value="2">
潮湿
</el-radio>
<el-radio :value="3">
偶尔潮湿
</el-radio><el-radio :value="4">
很少潮湿
</el-radio>
<el-radio :value="1">{{ $t('nursing.enhancedAssessment.moisturePersistent') }}</el-radio>
<el-radio :value="2">{{ $t('nursing.enhancedAssessment.moistureWet') }}</el-radio>
<el-radio :value="3">{{ $t('nursing.enhancedAssessment.moistureOccasional') }}</el-radio>
<el-radio :value="4">{{ $t('nursing.enhancedAssessment.moistureRare') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="活动">
<el-form-item :label="$t('nursing.enhancedAssessment.activity')">
<el-radio-group v-model="bradenScores.activity">
<el-radio :value="1">
卧床不起
</el-radio><el-radio :value="2">
限于轮椅
</el-radio>
<el-radio :value="3">
偶尔步行
</el-radio><el-radio :value="4">
经常步行
</el-radio>
<el-radio :value="1">{{ $t('nursing.enhancedAssessment.activityBed') }}</el-radio>
<el-radio :value="2">{{ $t('nursing.enhancedAssessment.activityWheelchair') }}</el-radio>
<el-radio :value="3">{{ $t('nursing.enhancedAssessment.activityOccasionalWalk') }}</el-radio>
<el-radio :value="4">{{ $t('nursing.enhancedAssessment.activityFrequentWalk') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="移动">
<el-form-item :label="$t('nursing.enhancedAssessment.mobility')">
<el-radio-group v-model="bradenScores.mobility">
<el-radio :value="1">
完全不能
</el-radio><el-radio :value="2">
严重受限
</el-radio>
<el-radio :value="3">
轻度受限
</el-radio><el-radio :value="4">
不受限
</el-radio>
<el-radio :value="1">{{ $t('nursing.enhancedAssessment.mobilityNone') }}</el-radio>
<el-radio :value="2">{{ $t('nursing.enhancedAssessment.mobilitySevere') }}</el-radio>
<el-radio :value="3">{{ $t('nursing.enhancedAssessment.mobilityMild') }}</el-radio>
<el-radio :value="4">{{ $t('nursing.enhancedAssessment.mobilityUnlimited') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="营养">
<el-form-item :label="$t('nursing.enhancedAssessment.nutrition')">
<el-radio-group v-model="bradenScores.nutrition">
<el-radio :value="1">
非常差
</el-radio><el-radio :value="2">
可能不足
</el-radio>
<el-radio :value="3">
充足
</el-radio><el-radio :value="4">
丰富
</el-radio>
<el-radio :value="1">{{ $t('nursing.enhancedAssessment.nutritionVeryPoor') }}</el-radio>
<el-radio :value="2">{{ $t('nursing.enhancedAssessment.nutritionProbablyInsufficient') }}</el-radio>
<el-radio :value="3">{{ $t('nursing.enhancedAssessment.nutritionAdequate') }}</el-radio>
<el-radio :value="4">{{ $t('nursing.enhancedAssessment.nutritionRich') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="摩擦力">
<el-form-item :label="$t('nursing.enhancedAssessment.friction')">
<el-radio-group v-model="bradenScores.friction">
<el-radio :value="1">
存在问题
</el-radio><el-radio :value="2">
有潜在问题
</el-radio>
<el-radio :value="3">
无明显问题
</el-radio>
<el-radio :value="1">{{ $t('nursing.enhancedAssessment.frictionProblem') }}</el-radio>
<el-radio :value="2">{{ $t('nursing.enhancedAssessment.frictionPotential') }}</el-radio>
<el-radio :value="3">{{ $t('nursing.enhancedAssessment.frictionNone') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="总分">
<el-tag
size="large"
:type="bradenTotal<=12?'danger':bradenTotal<=14?'warning':'success'"
>
{{ bradenTotal }} ({{ bradenTotal<=12?'高危':bradenTotal<=14?'中危':'低危' }})
<el-form-item :label="$t('nursing.enhancedAssessment.totalScoreLabel')">
<el-tag size="large" :type="bradenTotal<=12?'danger':bradenTotal<=14?'warning':'success'">
{{ bradenTotal }}{{ $t('nursing.mobile.scoreLabel') }} ({{ bradenTotal<=12 ? $t('nursing.enhancedAssessment.high') : bradenTotal<=14 ? $t('nursing.enhancedAssessment.medium') : $t('nursing.enhancedAssessment.low') }})
</el-tag>
</el-form-item>
</template>
<!-- Morse量表 -->
<template v-if="assessForm.assessmentTool==='MORSE'">
<el-divider content-position="left">
Morse跌倒风险评估量表
</el-divider>
<el-form-item label="跌倒史">
<el-divider content-position="left">{{ $t('nursing.enhancedAssessment.morseScale') }}</el-divider>
<el-form-item :label="$t('nursing.enhancedAssessment.fallHistory')">
<el-radio-group v-model="morseScores.history">
<el-radio :value="0">
</el-radio><el-radio :value="15">
</el-radio>
<el-radio :value="0">{{ $t('nursing.enhancedAssessment.no') }}</el-radio>
<el-radio :value="15">{{ $t('nursing.enhancedAssessment.yes') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="诊断">
<el-form-item :label="$t('nursing.enhancedAssessment.diagnosis')">
<el-radio-group v-model="morseScores.diagnosis">
<el-radio :value="0">
</el-radio><el-radio :value="15">
(2)
</el-radio>
<el-radio :value="0">{{ $t('nursing.enhancedAssessment.no') }}</el-radio>
<el-radio :value="15">{{ $t('nursing.enhancedAssessment.multiDiagnosis') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="行走辅助">
<el-form-item :label="$t('nursing.enhancedAssessment.ambulation')">
<el-radio-group v-model="morseScores.ambulation">
<el-radio :value="0">
/卧床/轮椅
</el-radio><el-radio :value="15">
拐杖/助行器
</el-radio>
<el-radio :value="30">
扶家具
</el-radio>
<el-radio :value="0">{{ $t('nursing.enhancedAssessment.noneOrWheelchair') }}</el-radio>
<el-radio :value="15">{{ $t('nursing.enhancedAssessment.crutchOrWalker') }}</el-radio>
<el-radio :value="30">{{ $t('nursing.enhancedAssessment.furniture') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="静脉输液">
<el-form-item :label="$t('nursing.enhancedAssessment.ivTherapy')">
<el-radio-group v-model="morseScores.iv">
<el-radio :value="0">
</el-radio><el-radio :value="20">
</el-radio>
<el-radio :value="0">{{ $t('nursing.enhancedAssessment.no') }}</el-radio>
<el-radio :value="20">{{ $t('nursing.enhancedAssessment.yes') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="步态">
<el-form-item :label="$t('nursing.enhancedAssessment.gait')">
<el-radio-group v-model="morseScores.gait">
<el-radio :value="0">
正常/卧床/轮椅
</el-radio><el-radio :value="10">
虚弱
</el-radio>
<el-radio :value="20">
障碍
</el-radio>
<el-radio :value="0">{{ $t('nursing.enhancedAssessment.normalOrWheelchair') }}</el-radio>
<el-radio :value="10">{{ $t('nursing.enhancedAssessment.weak') }}</el-radio>
<el-radio :value="20">{{ $t('nursing.enhancedAssessment.impaired') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="精神状态">
<el-form-item :label="$t('nursing.enhancedAssessment.mentalState')">
<el-radio-group v-model="morseScores.mental">
<el-radio :value="0">
正确评估自身能力
</el-radio><el-radio :value="15">
高估/忘记限制
</el-radio>
<el-radio :value="0">{{ $t('nursing.enhancedAssessment.correctSelfAssessment') }}</el-radio>
<el-radio :value="15">{{ $t('nursing.enhancedAssessment.overestimate') }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="总分">
<el-tag
size="large"
:type="morseTotal>=45?'danger':morseTotal>=25?'warning':'success'"
>
{{ morseTotal }} ({{ morseTotal>=45?'高危':morseTotal>=25?'中危':'低危' }})
<el-form-item :label="$t('nursing.enhancedAssessment.totalScoreLabel')">
<el-tag size="large" :type="morseTotal>=45?'danger':morseTotal>=25?'warning':'success'">
{{ morseTotal }}{{ $t('nursing.mobile.scoreLabel') }} ({{ morseTotal>=45 ? $t('nursing.enhancedAssessment.high') : morseTotal>=25 ? $t('nursing.enhancedAssessment.medium') : $t('nursing.enhancedAssessment.low') }})
</el-tag>
</el-form-item>
</template>
<el-form-item label="评估备注">
<el-input
v-model="assessForm.detail"
type="textarea"
:rows="2"
placeholder="请输入评估备注"
/>
<el-form-item :label="$t('nursing.enhancedAssessment.assessmentRemark')">
<el-input v-model="assessForm.detail" type="textarea" :rows="2" :placeholder="$t('nursing.enhancedAssessment.remarkPlaceholder')" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="assessVisible = false">
取消
</el-button>
<el-button
type="primary"
@click="submitAssess"
>
提交评估
</el-button>
<el-button @click="assessVisible = false">{{ $t('nursing.enhancedAssessment.cancel') }}</el-button>
<el-button type="primary" @click="submitAssess">{{ $t('nursing.enhancedAssessment.submitAssessment') }}</el-button>
</template>
</el-dialog>
</div>
@@ -616,42 +261,54 @@
<script setup>
import {ref, reactive, computed, onMounted} from 'vue'
import {ElMessage} from 'element-plus'
import {useI18n} from 'vue-i18n'
import {getPage, bradenAssess, morseAssess, nrs2002Assess, painAssess, tubeAssess, getInterventionPage, getStats} from './api'
const {t} = useI18n()
const activeTab = ref('BRADEN')
const assessVisible = ref(false)
const bradenData = ref([]), morseData = ref([]), nrsData = ref([]), painData = ref([]), tubeData = ref([])
const interventionData = ref([])
const statCards = ref([
{label:'总评估', value:0, color:'#409eff'},
{label:'高危患者', value:0, color:'#f56c6c'},
{label:'中危患者', value:0, color:'#e6a23c'},
{label:'已干预', value:0, color:'#67c23a'},
{label:'待评估', value:0, color:'#909399'},
{label:'评估率', value:'0%', color:'#409eff'}
const statCardsRaw = ref([
{value: 0}, {value: 0}, {value: 0}, {value: 0}, {value: 0}, {value: '0%'}
])
const assessForm = reactive({patientName:'', encounterId:'', assessmentTool:'BRADEN', detail:''})
const bradenScores = reactive({sensation:4, moisture:4, activity:4, mobility:4, nutrition:4, friction:3})
const morseScores = reactive({history:0, diagnosis:0, ambulation:0, iv:0, gait:0, mental:0})
const statCardsComputed = computed(() => [
{label: t('nursing.enhancedAssessment.totalAssessments'), value: statCardsRaw.value[0].value, color: '#409eff'},
{label: t('nursing.enhancedAssessment.highRiskPatients'), value: statCardsRaw.value[1].value, color: '#f56c6c'},
{label: t('nursing.enhancedAssessment.mediumRiskPatients'), value: statCardsRaw.value[2].value, color: '#e6a23c'},
{label: t('nursing.enhancedAssessment.intervened'), value: statCardsRaw.value[3].value, color: '#67c23a'},
{label: t('nursing.enhancedAssessment.pendingAssessment'), value: statCardsRaw.value[4].value, color: '#909399'},
{label: t('nursing.enhancedAssessment.assessmentRate'), value: statCardsRaw.value[5].value, color: '#409eff'}
])
const assessForm = reactive({patientName: '', encounterId: '', assessmentTool: 'BRADEN', detail: ''})
const bradenScores = reactive({sensation: 4, moisture: 4, activity: 4, mobility: 4, nutrition: 4, friction: 3})
const morseScores = reactive({history: 0, diagnosis: 0, ambulation: 0, iv: 0, gait: 0, mental: 0})
const bradenTotal = computed(() => bradenScores.sensation + bradenScores.moisture + bradenScores.activity + bradenScores.mobility + bradenScores.nutrition + bradenScores.friction)
const morseTotal = computed(() => morseScores.history + morseScores.diagnosis + morseScores.ambulation + morseScores.iv + morseScores.gait + morseScores.mental)
const assessDialogTitle = computed(() => {
const m = {BRADEN:'Braden压疮风险评估', MORSE:'Morse跌倒风险评估', NRS2002:'NRS2002营养筛查', PAIN:'疼痛评估', TUBE:'管道风险评估'}
return m[assessForm.assessmentTool] || '护理评估'
const m = {
BRADEN: 'Braden ' + t('nursing.enhancedAssessment.bradenScale'),
MORSE: 'Morse ' + t('nursing.enhancedAssessment.morseScale'),
NRS2002: 'NRS2002',
PAIN: t('nursing.enhancedAssessment.painTab'),
TUBE: t('nursing.enhancedAssessment.tubeTab')
}
return m[assessForm.assessmentTool] || t('nursing.enhancedAssessment.title')
})
async function loadStats() {
try {
const r = await getStats()
if (r.data) {
statCards.value[0].value = r.data.total || 0
statCards.value[1].value = r.data.highRisk || 0
statCards.value[2].value = r.data.mediumRisk || 0
statCards.value[3].value = r.data.intervened || 0
statCards.value[4].value = r.data.pending || 0
statCards.value[5].value = (r.data.assessmentRate || 0) + '%'
statCardsRaw.value[0].value = r.data.total || 0
statCardsRaw.value[1].value = r.data.highRisk || 0
statCardsRaw.value[2].value = r.data.mediumRisk || 0
statCardsRaw.value[3].value = r.data.intervened || 0
statCardsRaw.value[4].value = r.data.pending || 0
statCardsRaw.value[5].value = (r.data.assessmentRate || 0) + '%'
}
} catch(e) {}
}
@@ -660,74 +317,44 @@ async function loadData() {
const tools = ['BRADEN','MORSE','NRS2002','PAIN','TUBE']
const targets = [bradenData, morseData, nrsData, painData, tubeData]
for (let i = 0; i < tools.length; i++) {
try {
const r = await getPage({assessmentTool: tools[i], pageNo:1, pageSize:50})
targets[i].value = r.data?.records || []
} catch(e) { targets[i].value = [] }
try { const r = await getPage({assessmentTool: tools[i], pageNo: 1, pageSize: 50}); targets[i].value = r.data?.records || [] } catch(e) { targets[i].value = [] }
}
try {
const r = await getInterventionPage({pageNo:1, pageSize:50})
interventionData.value = r.data?.records || []
} catch(e) {}
try { const r = await getInterventionPage({pageNo: 1, pageSize: 50}); interventionData.value = r.data?.records || [] } catch(e) {}
}
function openAssess(tool) {
assessForm.patientName = ''
assessForm.encounterId = ''
assessForm.assessmentTool = tool
assessForm.detail = ''
if (tool === 'BRADEN') {
Object.assign(bradenScores, {sensation:4, moisture:4, activity:4, mobility:4, nutrition:4, friction:3})
} else if (tool === 'MORSE') {
Object.assign(morseScores, {history:0, diagnosis:0, ambulation:0, iv:0, gait:0, mental:0})
}
assessForm.patientName = ''; assessForm.encounterId = ''; assessForm.assessmentTool = tool; assessForm.detail = ''
if (tool === 'BRADEN') { Object.assign(bradenScores, {sensation: 4, moisture: 4, activity: 4, mobility: 4, nutrition: 4, friction: 3}) }
else if (tool === 'MORSE') { Object.assign(morseScores, {history: 0, diagnosis: 0, ambulation: 0, iv: 0, gait: 0, mental: 0}) }
assessVisible.value = true
}
async function submitAssess() {
const data = {
patientName: assessForm.patientName,
encounterId: assessForm.encounterId,
detail: assessForm.detail
}
const data = { patientName: assessForm.patientName, encounterId: assessForm.encounterId, detail: assessForm.detail }
try {
if (assessForm.assessmentTool === 'BRADEN') {
data.itemScores = JSON.stringify({sensation:bradenScores.sensation, moisture:bradenScores.moisture, activity:bradenScores.activity, mobility:bradenScores.mobility, nutrition:bradenScores.nutrition, friction:bradenScores.friction}); data.totalScore = bradenTotal.value
data.sensation = bradenScores.sensation
data.moisture = bradenScores.moisture
data.activity = bradenScores.activity
data.mobility = bradenScores.mobility
data.nutrition = bradenScores.nutrition
data.friction = bradenScores.friction
data.itemScores = JSON.stringify({sensation: bradenScores.sensation, moisture: bradenScores.moisture, activity: bradenScores.activity, mobility: bradenScores.mobility, nutrition: bradenScores.nutrition, friction: bradenScores.friction}); data.totalScore = bradenTotal.value
data.sensation = bradenScores.sensation; data.moisture = bradenScores.moisture; data.activity = bradenScores.activity
data.mobility = bradenScores.mobility; data.nutrition = bradenScores.nutrition; data.friction = bradenScores.friction
await bradenAssess(data)
} else if (assessForm.assessmentTool === 'MORSE') {
data.itemScores = JSON.stringify({history:morseScores.history, diagnosis:morseScores.diagnosis, ambulation:morseScores.ambulation, iv:morseScores.iv, gait:morseScores.gait, mental:morseScores.mental}); data.totalScore = morseTotal.value
data.history = morseScores.history
data.diagnosis = morseScores.diagnosis
data.ambulation = morseScores.ambulation
data.iv = morseScores.iv
data.gait = morseScores.gait
data.mental = morseScores.mental
data.itemScores = JSON.stringify({history: morseScores.history, diagnosis: morseScores.diagnosis, ambulation: morseScores.ambulation, iv: morseScores.iv, gait: morseScores.gait, mental: morseScores.mental}); data.totalScore = morseTotal.value
data.history = morseScores.history; data.diagnosis = morseScores.diagnosis; data.ambulation = morseScores.ambulation
data.iv = morseScores.iv; data.gait = morseScores.gait; data.mental = morseScores.mental
await morseAssess(data)
} else if (assessForm.assessmentTool === 'NRS2002') {
await nrs2002Assess(data)
} else if (assessForm.assessmentTool === 'PAIN') {
await painAssess(data)
} else if (assessForm.assessmentTool === 'TUBE') {
await tubeAssess(data)
}
ElMessage.success('评估提交成功')
} else if (assessForm.assessmentTool === 'NRS2002') { await nrs2002Assess(data) }
else if (assessForm.assessmentTool === 'PAIN') { await painAssess(data) }
else if (assessForm.assessmentTool === 'TUBE') { await tubeAssess(data) }
ElMessage.success(t('nursing.enhancedAssessment.submitSuccess'))
assessVisible.value = false
loadData()
loadStats()
} catch(e) {
ElMessage.error('提交失败: ' + (e.message || '未知错误'))
ElMessage.error(t('nursing.enhancedAssessment.submitFailed', { error: e.message || t('nursing.mobile.unknown') }))
}
}
function exportReport() {
ElMessage.info('导出功能开发中')
}
function exportReport() { ElMessage.info(t('nursing.enhancedAssessment.exportDev')) }
onMounted(() => { loadData(); loadStats() })
</script>

View File

@@ -1,278 +1,181 @@
<template>
<div style="padding:16px">
<div style="margin-bottom:16px">
<span style="font-size:18px;font-weight:bold">护理评估增强</span>
<span style="font-size:18px;font-weight:bold">{{ $t('nursing.enhanced.title') }}</span>
</div>
<el-tabs
v-model="tab"
type="border-card"
>
<el-tab-pane
label="评估提醒"
name="reminder"
>
<el-card
shadow="never"
style="margin-bottom:12px"
>
<el-tabs v-model="tab" type="border-card">
<el-tab-pane :label="$t('nursing.enhanced.reminderTab')" name="reminder">
<el-card shadow="never" style="margin-bottom:12px">
<div style="display:flex;gap:40px;text-align:center">
<div>
<div style="font-size:28px;font-weight:bold;color:#e6a23c">
{{ overdueCount }}
</div><div>逾期未评估</div>
<div style="font-size:28px;font-weight:bold;color:#e6a23c">{{ overdueCount }}</div>
<div>{{ $t('nursing.enhanced.overdue') }}</div>
</div>
<div>
<div style="font-size:28px;font-weight:bold;color:#67c23a">
{{ qualityStats.completedReminders||0 }}
</div><div>已完成评估</div>
<div style="font-size:28px;font-weight:bold;color:#67c23a">{{ qualityStats.completedReminders||0 }}</div>
<div>{{ $t('nursing.enhanced.completedAssessment') }}</div>
</div>
</div>
</el-card>
<el-table
:data="reminderData"
border
stripe
>
<el-table-column
prop="patientName"
label="患者"
width="90"
/>
<el-table-column
prop="assessmentType"
label="评估类型"
width="110"
>
<el-table :data="reminderData" border stripe>
<el-table-column prop="patientName" :label="$t('nursing.enhanced.patient')" width="90" />
<el-table-column prop="assessmentType" :label="$t('nursing.enhanced.assessmentType')" width="110">
<template #default="{row}">
{{ {fall_risk:'跌倒风险',pressure_ulcer:'压疮风险',nutrition:'营养筛查',pain:'疼痛评估',pipeline:'管道评估'}[row.assessmentType]||row.assessmentType }}
{{ assessmentTypeMap[row.assessmentType] || row.assessmentType }}
</template>
</el-table-column>
<el-table-column
prop="frequencyHours"
label="频率(小时)"
width="100"
align="center"
/>
<el-table-column
prop="nextDeadline"
label="下次截止"
width="170"
/>
<el-table-column
prop="status"
label="状态"
width="90"
>
<el-table-column prop="frequencyHours" :label="$t('nursing.enhanced.frequencyHours')" width="100" align="center" />
<el-table-column prop="nextDeadline" :label="$t('nursing.enhanced.nextDeadline')" width="170" />
<el-table-column prop="status" :label="$t('nursing.enhanced.status')" width="90">
<template #default="{row}">
<el-tag
:type="['warning','success','danger'][row.status]"
size="small"
>
{{ ['待评估','已评估','已超时'][row.status] }}
<el-tag :type="['warning','success','danger'][row.status]" size="small">
{{ [$t('nursing.enhanced.pendingAssessment'),$t('nursing.enhanced.assessed'),$t('nursing.enhanced.overdueStatus')][row.status] }}
</el-tag>
</template>
</el-table-column>
<el-table-column
label="操作"
width="120"
>
<el-table-column :label="$t('common.operation')" width="120">
<template #default="{row}">
<el-button
v-if="row.status!==1"
type="success"
link
size="small"
@click="completeReminderAction(row)"
>
完成评估
<el-button v-if="row.status!==1" type="success" link size="small" @click="completeReminderAction(row)">
{{ $t('nursing.enhanced.completeAssessment') }}
</el-button>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
<el-tab-pane
label="护理计划"
name="carePlan"
>
<el-tab-pane :label="$t('nursing.enhanced.carePlanTab')" name="carePlan">
<div style="margin-bottom:12px">
<el-button
type="success"
@click="showCarePlan=true"
>
新增护理计划
</el-button>
<el-button type="success" @click="showCarePlan=true">{{ $t('nursing.enhanced.addCarePlan') }}</el-button>
</div>
<el-table
:data="carePlanData"
border
stripe
>
<el-table-column
prop="patientName"
label="患者"
width="90"
/>
<el-table-column
prop="nursingDiagnosis"
label="护理诊断"
width="150"
/>
<el-table-column
prop="nursingGoal"
label="护理目标"
min-width="180"
show-overflow-tooltip
/>
<el-table-column
prop="nursingIntervention"
label="护理措施"
min-width="180"
show-overflow-tooltip
/>
<el-table-column
prop="status"
label="状态"
width="80"
>
<el-table :data="carePlanData" border stripe>
<el-table-column prop="patientName" :label="$t('nursing.enhanced.patient')" width="90" />
<el-table-column prop="nursingDiagnosis" :label="$t('nursing.enhanced.nursingDiagnosis')" width="150" />
<el-table-column prop="nursingGoal" :label="$t('nursing.enhanced.nursingGoal')" min-width="180" show-overflow-tooltip />
<el-table-column prop="nursingIntervention" :label="$t('nursing.enhanced.nursingIntervention')" min-width="180" show-overflow-tooltip />
<el-table-column prop="status" :label="$t('nursing.enhanced.status')" width="80">
<template #default="{row}">
<el-tag
:type="['info','','success',''][row.status]"
size="small"
>
{{ ['新建','执行中','已完成','已停止'][row.status] }}
<el-tag :type="['info','','success',''][row.status]" size="small">
{{ [$t('nursing.enhanced.newStatus'),$t('nursing.enhanced.executing'),$t('nursing.enhanced.completed'),$t('nursing.enhanced.stopped')][row.status] }}
</el-tag>
</template>
</el-table-column>
<el-table-column
label="操作"
width="100"
>
<el-table-column :label="$t('common.operation')" width="100">
<template #default="{row}">
<el-button
v-if="row.status<2"
type="primary"
link
size="small"
@click="evaluatePlan(row)"
>
评价
<el-button v-if="row.status<2" type="primary" link size="small" @click="evaluatePlan(row)">
{{ $t('nursing.enhanced.evaluate') }}
</el-button>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
<el-tab-pane
label="质量指标"
name="quality"
>
<el-tab-pane :label="$t('nursing.enhanced.qualityTab')" name="quality">
<el-card shadow="never">
<div style="display:flex;gap:30px;text-align:center;flex-wrap:wrap">
<div>
<div style="font-size:24px;font-weight:bold;color:#409eff">
{{ qualityStats.totalReminders||0 }}
</div><div>总评估提醒</div>
<div style="font-size:24px;font-weight:bold;color:#409eff">{{ qualityStats.totalReminders||0 }}</div>
<div>{{ $t('nursing.enhanced.totalReminders') }}</div>
</div>
<div>
<div style="font-size:24px;font-weight:bold;color:#67c23a">
{{ qualityStats.completedReminders||0 }}
</div><div>已完成</div>
<div style="font-size:24px;font-weight:bold;color:#67c23a">{{ qualityStats.completedReminders||0 }}</div>
<div>{{ $t('nursing.enhanced.totalCompleted') }}</div>
</div>
<div>
<div style="font-size:24px;font-weight:bold;color:#f56c6c">
{{ qualityStats.overdueReminders||0 }}
</div><div>已超时</div>
<div style="font-size:24px;font-weight:bold;color:#f56c6c">{{ qualityStats.overdueReminders||0 }}</div>
<div>{{ $t('nursing.enhanced.totalOverdue') }}</div>
</div>
<div>
<div style="font-size:24px;font-weight:bold;color:#409eff">
{{ qualityStats.totalPlans||0 }}
</div><div>总护理计划</div>
<div style="font-size:24px;font-weight:bold;color:#409eff">{{ qualityStats.totalPlans||0 }}</div>
<div>{{ $t('nursing.enhanced.totalPlans') }}</div>
</div>
<div>
<div style="font-size:24px;font-weight:bold;color:#67c23a">
{{ qualityStats.completedPlans||0 }}
</div><div>已完成计划</div>
<div style="font-size:24px;font-weight:bold;color:#67c23a">{{ qualityStats.completedPlans||0 }}</div>
<div>{{ $t('nursing.enhanced.completedPlans') }}</div>
</div>
</div>
</el-card>
</el-tab-pane>
</el-tabs>
<el-dialog
v-model="showCarePlan"
title="新增护理计划"
width="600px"
>
<el-form
:model="carePlanForm"
label-width="100px"
>
<el-form-item label="患者">
<el-dialog v-model="showCarePlan" :title="$t('nursing.enhanced.addCarePlanTitle')" width="600px">
<el-form :model="carePlanForm" label-width="100px">
<el-form-item :label="$t('nursing.enhanced.patient')">
<el-input v-model="carePlanForm.patientName" />
</el-form-item>
<el-form-item label="就诊ID">
<el-input-number
v-model="carePlanForm.encounterId"
:min="1"
/>
<el-form-item :label="$t('nursing.enhanced.encounterId')">
<el-input-number v-model="carePlanForm.encounterId" :min="1" />
</el-form-item>
<el-form-item label="护理诊断">
<el-form-item :label="$t('nursing.enhanced.nursingDiagnosis')">
<el-input v-model="carePlanForm.nursingDiagnosis" />
</el-form-item>
<el-form-item label="护理目标">
<el-input
v-model="carePlanForm.nursingGoal"
type="textarea"
/>
<el-form-item :label="$t('nursing.enhanced.nursingGoal')">
<el-input v-model="carePlanForm.nursingGoal" type="textarea" />
</el-form-item>
<el-form-item label="护理措施">
<el-input
v-model="carePlanForm.nursingIntervention"
type="textarea"
/>
<el-form-item :label="$t('nursing.enhanced.nursingIntervention')">
<el-input v-model="carePlanForm.nursingIntervention" type="textarea" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="showCarePlan=false">
取消
</el-button>
<el-button
type="primary"
@click="submitCarePlan"
>
保存
</el-button>
<el-button @click="showCarePlan=false">{{ $t('nursing.enhanced.cancel') }}</el-button>
<el-button type="primary" @click="submitCarePlan">{{ $t('nursing.enhanced.save') }}</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup>
import {ref,reactive,onMounted} from 'vue'
import {ElMessage,ElMessageBox} from 'element-plus'
import {getReminderPage,completeReminder,getCarePlanPage,addCarePlan,evaluateCarePlan,getQualityStats} from './api'
import {ref, reactive, computed, onMounted} from 'vue'
import {ElMessage, ElMessageBox} from 'element-plus'
import {useI18n} from 'vue-i18n'
import {getReminderPage, completeReminder, getCarePlanPage, addCarePlan, evaluateCarePlan, getQualityStats} from './api'
const tab=ref('reminder')
const reminderData=ref([]),carePlanData=ref([])
const overdueCount=ref(0)
const qualityStats=ref({})
const showCarePlan=ref(false)
const carePlanForm=reactive({patientName:'',encounterId:null,nursingDiagnosis:'',nursingGoal:'',nursingIntervention:''})
const {t} = useI18n()
const tab = ref('reminder')
const reminderData = ref([]), carePlanData = ref([])
const overdueCount = ref(0)
const qualityStats = ref({})
const showCarePlan = ref(false)
const carePlanForm = reactive({patientName: '', encounterId: null, nursingDiagnosis: '', nursingGoal: '', nursingIntervention: ''})
const loadData=async()=>{
const [r,cp,q]=await Promise.all([
getReminderPage({pageNo:1,pageSize:50}),getCarePlanPage({pageNo:1,pageSize:50}),getQualityStats({})
const assessmentTypeMap = computed(() => ({
fall_risk: t('nursing.enhanced.fallRisk'),
pressure_ulcer: t('nursing.enhanced.pressureUlcer'),
nutrition: t('nursing.enhanced.nutrition'),
pain: t('nursing.enhanced.pain'),
pipeline: t('nursing.enhanced.pipeline')
}))
const loadData = async () => {
const [r, cp, q] = await Promise.all([
getReminderPage({pageNo: 1, pageSize: 50}),
getCarePlanPage({pageNo: 1, pageSize: 50}),
getQualityStats({})
])
reminderData.value=r.data?.records||[];carePlanData.value=cp.data?.records||[]
qualityStats.value=q.data||{}
overdueCount.value=reminderData.value.filter(x=>x.status===2).length
reminderData.value = r.data?.records || []
carePlanData.value = cp.data?.records || []
qualityStats.value = q.data || {}
overdueCount.value = reminderData.value.filter(x => x.status === 2).length
}
const completeReminderAction=async(row)=>{await completeReminder(row.id);ElMessage.success('评估完成');loadData()}
const evaluatePlan=async(row)=>{
const{value}=await ElMessageBox.prompt('请输入评价结果','评价护理计划',{inputType:'textarea'})
await evaluateCarePlan({id:row.id,evaluationResult:value});ElMessage.success('评价完成');loadData()
const completeReminderAction = async (row) => {
await completeReminder(row.id)
ElMessage.success(t('nursing.enhanced.assessmentComplete'))
loadData()
}
const submitCarePlan=async()=>{await addCarePlan(carePlanForm);ElMessage.success('新增成功');showCarePlan.value=false;loadData()}
onMounted(()=>loadData())
const evaluatePlan = async (row) => {
const {value} = await ElMessageBox.prompt(t('nursing.enhanced.evaluatePrompt'), t('nursing.enhanced.evaluateTitle'), {inputType: 'textarea'})
await evaluateCarePlan({id: row.id, evaluationResult: value})
ElMessage.success(t('nursing.enhanced.evaluateComplete'))
loadData()
}
const submitCarePlan = async () => {
await addCarePlan(carePlanForm)
ElMessage.success(t('nursing.enhanced.addSuccess'))
showCarePlan.value = false
loadData()
}
onMounted(() => loadData())
</script>

View File

@@ -1,267 +1,106 @@
<template>
<div style="padding:16px">
<div style="margin-bottom:16px">
<span style="font-size:18px;font-weight:bold">住院护士执行</span>
<span style="font-size:18px;font-weight:bold">{{ $t('nursing.execution.title') }}</span>
</div>
<el-tabs
v-model="tab"
type="border-card"
>
<el-tab-pane
label="执行扫码"
name="scan"
>
<el-tabs v-model="tab" type="border-card">
<el-tab-pane :label="$t('nursing.execution.scanTab')" name="scan">
<div style="margin-bottom:12px">
<el-button
type="success"
@click="showScan=true"
>
新增扫码
</el-button>
<el-button type="success" @click="showScan=true">{{ $t('nursing.execution.addScan') }}</el-button>
</div>
<el-table
:data="scanData"
border
stripe
>
<el-table-column
prop="patientName"
label="患者"
width="90"
/>
<el-table-column
prop="scanType"
label="类型"
width="80"
/>
<el-table-column
prop="orderName"
label="医嘱"
min-width="150"
show-overflow-tooltip
/>
<el-table-column
prop="barcode"
label="条码"
width="130"
/>
<el-table-column
prop="scanResult"
label="结果"
width="80"
>
<el-table :data="scanData" border stripe>
<el-table-column prop="patientName" :label="$t('nursing.execution.patient')" width="90" />
<el-table-column prop="scanType" :label="$t('nursing.execution.type')" width="80" />
<el-table-column prop="orderName" :label="$t('nursing.execution.order')" min-width="150" show-overflow-tooltip />
<el-table-column prop="barcode" :label="$t('nursing.execution.barcode')" width="130" />
<el-table-column prop="scanResult" :label="$t('nursing.execution.result')" width="80">
<template #default="{row}">
<el-tag
:type="row.scanResult==='SUCCESS'?'success':'danger'"
size="small"
>
{{ row.scanResult==='SUCCESS'?'成功':'失败' }}
<el-tag :type="row.scanResult==='SUCCESS'?'success':'danger'" size="small">
{{ row.scanResult==='SUCCESS' ? $t('nursing.execution.success') : $t('nursing.execution.failed') }}
</el-tag>
</template>
</el-table-column>
<el-table-column
prop="nurseName"
label="护士"
width="80"
/>
<el-table-column
prop="scanTime"
label="时间"
width="170"
/>
<el-table-column prop="nurseName" :label="$t('nursing.execution.nurse')" width="80" />
<el-table-column prop="scanTime" :label="$t('nursing.execution.time')" width="170" />
</el-table>
</el-tab-pane>
<el-tab-pane
label="交接班"
name="handoff"
>
<el-tab-pane :label="$t('nursing.execution.handoffTab')" name="handoff">
<div style="margin-bottom:12px">
<el-button
type="success"
@click="showHandoff=true"
>
新增交接
</el-button>
<el-button type="success" @click="showHandoff=true">{{ $t('nursing.execution.addHandoff') }}</el-button>
</div>
<!-- 重点患者区域 -->
<div
v-if="keyPatients.length > 0"
style="margin-bottom:16px;background:#fdf6ec;border:1px solid #e6a23c;border-radius:6px;padding:12px"
>
<div style="font-weight:bold;color:#e6a23c;margin-bottom:8px">
重点患者提示
</div>
<div
v-for="(kp, idx) in keyPatients"
:key="idx"
style="margin-bottom:6px;font-size:13px"
>
<el-tag
type="warning"
size="small"
style="margin-right:6px"
>
{{ kp.ward }} {{ kp.shift }}
</el-tag>
<div v-if="keyPatients.length > 0" style="margin-bottom:16px;background:#fdf6ec;border:1px solid #e6a23c;border-radius:6px;padding:12px">
<div style="font-weight:bold;color:#e6a23c;margin-bottom:8px">{{ $t('nursing.execution.keyPatientAlert') }}</div>
<div v-for="(kp, idx) in keyPatients" :key="idx" style="margin-bottom:6px;font-size:13px">
<el-tag type="warning" size="small" style="margin-right:6px">{{ kp.ward }} {{ kp.shift }}</el-tag>
<span style="font-weight:500">{{ kp.keyPatients }}</span>
<span
v-if="kp.pendingMatters"
style="color:#999;margin-left:8px"
>待办: {{ kp.pendingMatters }}</span>
<span v-if="kp.pendingMatters" style="color:#999;margin-left:8px">{{ $t('nursing.execution.pending') }}: {{ kp.pendingMatters }}</span>
</div>
</div>
<el-table
:data="handoffData"
border
stripe
>
<el-table-column
prop="ward"
label="病区"
width="100"
/>
<el-table-column
prop="shift"
label="班次"
width="80"
/>
<el-table-column
prop="handoffNurseName"
label="交班人"
width="90"
/>
<el-table-column
prop="receiveNurseName"
label="接班人"
width="90"
/>
<el-table-column
prop="patientCount"
label="患者"
width="60"
align="center"
/>
<el-table-column
prop="criticalCount"
label="危重"
width="60"
align="center"
/>
<el-table-column
prop="keyPatients"
label="重点患者"
min-width="150"
show-overflow-tooltip
/>
<el-table-column
prop="status"
label="状态"
width="80"
>
<el-table :data="handoffData" border stripe>
<el-table-column prop="ward" :label="$t('nursing.execution.ward')" width="100" />
<el-table-column prop="shift" :label="$t('nursing.execution.shift')" width="80" />
<el-table-column prop="handoffNurseName" :label="$t('nursing.execution.handoffNurse')" width="90" />
<el-table-column prop="receiveNurseName" :label="$t('nursing.execution.receiveNurse')" width="90" />
<el-table-column prop="patientCount" :label="$t('nursing.execution.patientCount')" width="60" align="center" />
<el-table-column prop="criticalCount" :label="$t('nursing.execution.criticalCount')" width="60" align="center" />
<el-table-column prop="keyPatients" :label="$t('nursing.execution.keyPatients')" min-width="150" show-overflow-tooltip />
<el-table-column prop="status" :label="$t('nursing.execution.status')" width="80">
<template #default="{row}">
<el-tag
:type="['warning','','success'][row.status]"
size="small"
>
{{ ['草稿','已交接','已确认'][row.status] }}
<el-tag :type="['warning','','success'][row.status]" size="small">
{{ [$t('nursing.execution.draft'),$t('nursing.execution.handedOff'),$t('nursing.execution.confirmed')][row.status] }}
</el-tag>
</template>
</el-table-column>
<el-table-column
label="操作"
width="80"
>
<el-table-column :label="$t('nursing.execution.operation')" width="80">
<template #default="{row}">
<el-button
v-if="row.status<2"
type="primary"
link
size="small"
@click="confirmAction(row)"
>
确认
<el-button v-if="row.status<2" type="primary" link size="small" @click="confirmAction(row)">
{{ $t('nursing.execution.confirm') }}
</el-button>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
<el-tab-pane
label="输液巡视"
name="infusion"
>
<el-tab-pane :label="$t('nursing.execution.infusionTab')" name="infusion">
<div style="margin-bottom:12px">
<el-button
type="success"
@click="showInfusion=true"
>
新增巡视
</el-button>
<el-button type="success" @click="showInfusion=true">{{ $t('nursing.execution.addPatrol') }}</el-button>
</div>
<el-table
:data="infusionData"
border
stripe
>
<el-table-column
prop="patientName"
label="患者"
width="90"
/>
<el-table-column
prop="drugName"
label="药品"
min-width="150"
show-overflow-tooltip
/>
<el-table-column
prop="dripRate"
label="滴速"
width="70"
align="center"
/>
<el-table-column
prop="patencyStatus"
label="通畅"
width="80"
>
<el-table :data="infusionData" border stripe>
<el-table-column prop="patientName" :label="$t('nursing.execution.patient')" width="90" />
<el-table-column prop="drugName" :label="$t('nursing.execution.drug')" min-width="150" show-overflow-tooltip />
<el-table-column prop="dripRate" :label="$t('nursing.execution.dripRate')" width="70" align="center" />
<el-table-column prop="patencyStatus" :label="$t('nursing.execution.patency')" width="80">
<template #default="{row}">
<el-tag
:type="row.patencyStatus==='NORMAL'?'success':'danger'"
size="small"
>
{{ row.patencyStatus==='NORMAL'?'正常':'异常' }}
<el-tag :type="row.patencyStatus==='NORMAL'?'success':'danger'" size="small">
{{ row.patencyStatus==='NORMAL' ? $t('nursing.execution.normal') : $t('nursing.execution.abnormal') }}
</el-tag>
</template>
</el-table-column>
<el-table-column
prop="patrolNurseName"
label="巡视护士"
width="90"
/>
<el-table-column
prop="patrolTime"
label="时间"
width="170"
/>
<el-table-column prop="patrolNurseName" :label="$t('nursing.execution.patrolNurseLabel')" width="90" />
<el-table-column prop="patrolTime" :label="$t('nursing.execution.time')" width="170" />
</el-table>
</el-tab-pane>
</el-tabs>
</div>
</template>
<script setup>
import {ref,reactive,onMounted} from 'vue'
import {ref, onMounted} from 'vue'
import {ElMessage} from 'element-plus'
import {useI18n} from 'vue-i18n'
import {getScanPage,addScan,getHandoffPage,addHandoff,confirmHandoff,getKeyPatients,getInfusionPage,addInfusion} from './api'
const {t} = useI18n()
const tab=ref('scan')
const scanData=ref([]),handoffData=ref([]),infusionData=ref([]),keyPatients=ref([])
const showScan=ref(false),showHandoff=ref(false),showInfusion=ref(false)
const scanForm=reactive({patientName:'',scanType:'WRISTBAND',barcode:'',nurseName:''})
const handoffForm=reactive({ward:'',shift:'MORNING',handoffNurseName:'',receiveNurseName:'',patientCount:0,criticalCount:0,keyPatients:''})
const infusionForm=reactive({patientName:'',drugName:'',dripRate:0,patencyStatus:'NORMAL',patrolNurseName:''})
const scanForm=ref({patientName:'',scanType:'WRISTBAND',barcode:'',nurseName:''})
const handoffForm=ref({ward:'',shift:'MORNING',handoffNurseName:'',receiveNurseName:'',patientCount:0,criticalCount:0,keyPatients:''})
const infusionForm=ref({patientName:'',drugName:'',dripRate:0,patencyStatus:'NORMAL',patrolNurseName:''})
const loadData=async()=>{const [s,h,kp,i]=await Promise.all([getScanPage({pageNo:1,pageSize:50}),getHandoffPage({pageNo:1,pageSize:50}),getKeyPatients(),getInfusionPage({pageNo:1,pageSize:50})]);scanData.value=s.data?.records||[];handoffData.value=h.data?.records||[];keyPatients.value=kp.data||[];infusionData.value=i.data?.records||[]}
const confirmAction=async(row)=>{await confirmHandoff(row.id);ElMessage.success('已确认');loadData()}
const submitScan=async()=>{await addScan(scanForm);ElMessage.success('成功');showScan.value=false;loadData()}
const submitHandoff=async()=>{await addHandoff(handoffForm);ElMessage.success('成功');showHandoff.value=false;loadData()}
const submitInfusion=async()=>{await addInfusion(infusionForm);ElMessage.success('成功');showInfusion.value=false;loadData()}
const confirmAction=async(row)=>{await confirmHandoff(row.id);ElMessage.success(t('nursing.execution.confirmedMsg'));loadData()}
const submitScan=async()=>{await addScan(scanForm.value);ElMessage.success(t('common.success'));showScan.value=false;loadData()}
const submitHandoff=async()=>{await addHandoff(handoffForm.value);ElMessage.success(t('common.success'));showHandoff.value=false;loadData()}
const submitInfusion=async()=>{await addInfusion(infusionForm.value);ElMessage.success(t('common.success'));showInfusion.value=false;loadData()}
onMounted(()=>loadData())
</script>

View File

@@ -3,101 +3,95 @@
<div class="page-header">
<el-page-header @back="goBack">
<template #content>
<span class="page-title">{{ patientName }} - 输液管理</span>
<span class="page-title">{{ patientName }} - {{ $t('nursing.mobile.infusionManagement') }}</span>
</template>
</el-page-header>
<el-button type="primary" size="small" @click="showStartDialog">开始输液</el-button>
<el-button type="primary" size="small" @click="showStartDialog">{{ $t('nursing.mobile.startInfusion') }}</el-button>
</div>
<div v-loading="loading" class="infusion-list">
<div
v-for="item in infusionList"
:key="item.id"
class="infusion-card"
>
<div v-for="item in infusionList" :key="item.id" class="infusion-card">
<div class="card-header">
<span class="drug-name">{{ item.drugName || '输液' }}</span>
<el-tag :type="getStatusType(item.patencyStatus)" size="small">
{{ getStatusText(item.patencyStatus) }}
</el-tag>
<span class="drug-name">{{ item.drugName || $t('nursing.mobile.infusionManagement') }}</span>
<el-tag :type="getStatusType(item.patencyStatus)" size="small">{{ getStatusText(item.patencyStatus) }}</el-tag>
</div>
<div class="card-body">
<div class="info-row">
<span class="label">输液速度:</span>
<span class="label">{{ $t('nursing.mobile.infusionSpeed') }}:</span>
<span class="value">{{ item.infusionRate || '-' }}</span>
</div>
<div class="info-row">
<span class="label">总量:</span>
<span class="label">{{ $t('nursing.mobile.totalVolume') }}:</span>
<span class="value">{{ item.totalVolume ? item.totalVolume + 'ml' : '-' }}</span>
</div>
<div class="info-row">
<span class="label">开始时间:</span>
<span class="label">{{ $t('nursing.mobile.startInfusion') }}:</span>
<span class="value">{{ formatTime(item.startTime) }}</span>
</div>
<div class="info-row" v-if="item.patrolTime">
<span class="label">最近巡视:</span>
<span class="label">{{ $t('nursing.mobile.patrolRecord') }}:</span>
<span class="value">{{ formatTime(item.patrolTime) }}</span>
</div>
<div class="info-row" v-if="item.dripRate">
<span class="label">滴速:</span>
<span class="value">{{ item.dripRate }} /</span>
<span class="label">{{ $t('nursing.mobile.dripRateLabel') }}:</span>
<span class="value">{{ item.dripRate }} {{ $t('nursing.mobile.unitTimesPerMin') }}</span>
</div>
<div class="info-row" v-if="item.adverseReaction">
<span class="label">不良反应:</span>
<span class="label">{{ $t('nursing.mobile.adverseReaction') }}:</span>
<span class="value danger-text">{{ item.adverseReaction }}</span>
</div>
</div>
<div class="card-footer">
<el-button size="small" @click="showPatrolDialog(item)">巡视记录</el-button>
<el-button size="small" @click="showPatrolDialog(item)">{{ $t('nursing.mobile.patrolRecord') }}</el-button>
</div>
</div>
<el-empty v-if="!loading && infusionList.length === 0" description="暂无输液记录" />
<el-empty v-if="!loading && infusionList.length === 0" :description="$t('nursing.mobile.noInfusionRecords')" />
</div>
<el-dialog v-model="startDialogVisible" title="开始输液" width="400px">
<el-dialog v-model="startDialogVisible" :title="$t('nursing.mobile.startInfusionTitle')" width="400px">
<el-form :model="startForm" label-width="80px">
<el-form-item label="药品名称">
<el-input v-model="startForm.drugName" placeholder="请输入药品名称" />
<el-form-item :label="$t('nursing.mobile.drugName')">
<el-input v-model="startForm.drugName" :placeholder="$t('nursing.mobile.drugNamePlaceholder')" />
</el-form-item>
<el-form-item label="输液速度">
<el-input v-model="startForm.infusionRate" placeholder="如: 40滴/分" />
<el-form-item :label="$t('nursing.mobile.infusionSpeed')">
<el-input v-model="startForm.infusionRate" :placeholder="$t('nursing.mobile.infusionSpeedPlaceholder')" />
</el-form-item>
<el-form-item label="总量(ml)">
<el-form-item :label="$t('nursing.mobile.totalVolume')">
<el-input-number v-model="startForm.totalVolume" :min="0" style="width: 100%" />
</el-form-item>
<el-form-item label="护士姓名">
<el-input v-model="startForm.patrolNurseName" placeholder="执行护士" />
<el-form-item :label="$t('nursing.mobile.patrolNurse')">
<el-input v-model="startForm.patrolNurseName" :placeholder="$t('nursing.mobile.executeNurse')" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="startDialogVisible = false">取消</el-button>
<el-button type="primary" :loading="submitting" @click="handleStartInfusion">确认开始</el-button>
<el-button @click="startDialogVisible = false">{{ $t('common.cancel') }}</el-button>
<el-button type="primary" :loading="submitting" @click="handleStartInfusion">{{ $t('nursing.mobile.confirmStart') }}</el-button>
</template>
</el-dialog>
<el-dialog v-model="patrolDialogVisible" title="输液巡视" width="400px">
<el-dialog v-model="patrolDialogVisible" :title="$t('nursing.mobile.infusionPatrol')" width="400px">
<el-form :model="patrolForm" label-width="80px">
<el-form-item label="滴速(滴/分)">
<el-form-item :label="$t('nursing.mobile.dripRateLabel')">
<el-input-number v-model="patrolForm.dripRate" :min="0" style="width: 100%" />
</el-form-item>
<el-form-item label="通畅状态">
<el-form-item :label="$t('nursing.mobile.patencyStatus')">
<el-select v-model="patrolForm.patencyStatus" style="width: 100%">
<el-option label="正常" value="NORMAL" />
<el-option label="堵塞" value="OCCLUDED" />
<el-option label="静脉炎" value="PHLEBITIS" />
<el-option :label="$t('nursing.execution.normal')" value="NORMAL" />
<el-option :label="$t('nursing.mobile.occluded')" value="OCCLUDED" />
<el-option :label="$t('nursing.mobile.phlebitis')" value="PHLEBITIS" />
</el-select>
</el-form-item>
<el-form-item label="不良反应">
<el-input v-model="patrolForm.adverseReaction" placeholder="无则留空" />
<el-form-item :label="$t('nursing.mobile.adverseReaction')">
<el-input v-model="patrolForm.adverseReaction" :placeholder="$t('nursing.mobile.emptyLeaveBlank')" />
</el-form-item>
<el-form-item label="巡视护士">
<el-input v-model="patrolForm.patrolNurseName" placeholder="巡视护士" />
<el-form-item :label="$t('nursing.mobile.patrolNurseLabel')">
<el-input v-model="patrolForm.patrolNurseName" :placeholder="$t('nursing.mobile.patrolNurseLabel')" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="patrolDialogVisible = false">取消</el-button>
<el-button type="primary" :loading="submitting" @click="handlePatrol">确认记录</el-button>
<el-button @click="patrolDialogVisible = false">{{ $t('common.cancel') }}</el-button>
<el-button type="primary" :loading="submitting" @click="handlePatrol">{{ $t('nursing.mobile.confirmRecord') }}</el-button>
</template>
</el-dialog>
</div>
@@ -107,8 +101,10 @@
import { ref, reactive, onMounted } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { ElMessage } from 'element-plus'
import { useI18n } from 'vue-i18n'
import { getInfusionStatus, startInfusion, addInfusionPatrol } from './api'
const { t } = useI18n()
const route = useRoute()
const router = useRouter()
const loading = ref(false)
@@ -123,56 +119,26 @@ const startDialogVisible = ref(false)
const patrolDialogVisible = ref(false)
const currentInfusion = ref(null)
const startForm = reactive({
drugName: '',
infusionRate: '',
totalVolume: 250,
patrolNurseName: ''
})
const startForm = reactive({ drugName: '', infusionRate: '', totalVolume: 250, patrolNurseName: '' })
const patrolForm = reactive({ dripRate: null, patencyStatus: 'NORMAL', adverseReaction: '', patrolNurseName: '' })
const patrolForm = reactive({
dripRate: null,
patencyStatus: 'NORMAL',
adverseReaction: '',
patrolNurseName: ''
})
const goBack = () => {
router.push('/nursingmobile/patient-list')
}
const formatTime = (time) => {
if (!time) return '-'
const d = new Date(time)
return d.toLocaleString('zh-CN')
}
const getStatusType = (status) => {
const map = { NORMAL: 'success', OCCLUDED: 'danger', PHLEBITIS: 'warning' }
return map[status] || 'info'
}
const getStatusText = (status) => {
const map = { NORMAL: '正常', OCCLUDED: '堵塞', PHLEBITIS: '静脉炎' }
return map[status] || '未知'
}
const goBack = () => { router.push('/nursingmobile/patient-list') }
const formatTime = (time) => { if (!time) return '-'; const d = new Date(time); return d.toLocaleString() }
const getStatusType = (status) => ({ NORMAL: 'success', OCCLUDED: 'danger', PHLEBITIS: 'warning' })[status] || 'info'
const getStatusText = (status) => ({
NORMAL: t('nursing.execution.normal'),
OCCLUDED: t('nursing.mobile.occluded'),
PHLEBITIS: t('nursing.mobile.phlebitis')
})[status] || t('nursing.mobile.unknown')
const fetchInfusionList = async () => {
if (!patientId.value) return
loading.value = true
try {
const res = await getInfusionStatus(patientId.value)
infusionList.value = res.data || []
} finally {
loading.value = false
}
try { const res = await getInfusionStatus(patientId.value); infusionList.value = res.data || [] } finally { loading.value = false }
}
const showStartDialog = () => {
startForm.drugName = ''
startForm.infusionRate = ''
startForm.totalVolume = 250
startForm.patrolNurseName = ''
startForm.drugName = ''; startForm.infusionRate = ''; startForm.totalVolume = 250; startForm.patrolNurseName = ''
startDialogVisible.value = true
}
@@ -186,30 +152,20 @@ const showPatrolDialog = (item) => {
}
const handleStartInfusion = async () => {
if (!startForm.drugName) {
ElMessage.warning('请输入药品名称')
return
}
if (!startForm.drugName) { ElMessage.warning(t('nursing.assessment.valDrugName')); return }
submitting.value = true
try {
await startInfusion({
patientId: patientId.value ? Number(patientId.value) : null,
encounterId: encounterId.value ? Number(encounterId.value) : null,
patientName: patientName.value,
drugName: startForm.drugName,
infusionRate: startForm.infusionRate,
totalVolume: startForm.totalVolume,
patrolNurseName: startForm.patrolNurseName,
startTime: new Date()
drugName: startForm.drugName, infusionRate: startForm.infusionRate,
totalVolume: startForm.totalVolume, patrolNurseName: startForm.patrolNurseName, startTime: new Date()
})
ElMessage.success('输液已开始')
ElMessage.success(t('nursing.mobile.infusionStarted'))
startDialogVisible.value = false
fetchInfusionList()
} catch (e) {
ElMessage.error('操作失败')
} finally {
submitting.value = false
}
} catch (e) { ElMessage.error(t('nursing.mobile.operationFailed')) } finally { submitting.value = false }
}
const handlePatrol = async () => {
@@ -221,100 +177,30 @@ const handlePatrol = async () => {
patientName: patientName.value,
orderId: currentInfusion.value.orderId || currentInfusion.value.id,
drugName: currentInfusion.value.drugName,
dripRate: patrolForm.dripRate,
patencyStatus: patrolForm.patencyStatus,
adverseReaction: patrolForm.adverseReaction,
patrolNurseName: patrolForm.patrolNurseName,
patrolTime: new Date()
dripRate: patrolForm.dripRate, patencyStatus: patrolForm.patencyStatus,
adverseReaction: patrolForm.adverseReaction, patrolNurseName: patrolForm.patrolNurseName, patrolTime: new Date()
})
ElMessage.success('巡视记录已保存')
ElMessage.success(t('nursing.mobile.patrolRecordSaved'))
patrolDialogVisible.value = false
fetchInfusionList()
} catch (e) {
ElMessage.error('记录失败')
} finally {
submitting.value = false
}
} catch (e) { ElMessage.error(t('nursing.mobile.recordFailed')) } finally { submitting.value = false }
}
onMounted(() => {
fetchInfusionList()
})
onMounted(() => { fetchInfusionList() })
</script>
<style scoped>
.mobile-infusion {
padding: 16px;
}
.page-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
}
.page-title {
font-size: 16px;
font-weight: 600;
}
.infusion-list {
display: flex;
flex-direction: column;
gap: 12px;
}
.infusion-card {
background: #fff;
border-radius: 8px;
padding: 16px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
}
.drug-name {
font-size: 15px;
font-weight: 600;
}
.card-body {
display: flex;
flex-direction: column;
gap: 8px;
}
.info-row {
display: flex;
align-items: center;
gap: 8px;
}
.info-row .label {
color: #666;
font-size: 13px;
min-width: 70px;
}
.info-row .value {
font-size: 14px;
}
.danger-text {
color: #f56c6c;
}
.card-footer {
margin-top: 12px;
padding-top: 12px;
border-top: 1px solid #eee;
display: flex;
justify-content: flex-end;
}
.mobile-infusion { padding: 16px; }
.page-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px; }
.page-title { font-size: 16px; font-weight: 600; }
.infusion-list { display: flex; flex-direction: column; gap: 12px; }
.infusion-card { background: #fff; border-radius: 8px; padding: 16px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); }
.card-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px; }
.drug-name { font-size: 15px; font-weight: 600; }
.card-body { display: flex; flex-direction: column; gap: 8px; }
.info-row { display: flex; align-items: center; gap: 8px; }
.info-row .label { color: #666; font-size: 13px; min-width: 70px; }
.info-row .value { font-size: 14px; }
.danger-text { color: #f56c6c; }
.card-footer { margin-top: 12px; padding-top: 12px; border-top: 1px solid #eee; display: flex; justify-content: flex-end; }
</style>

View File

@@ -3,16 +3,16 @@
<div class="page-header">
<el-page-header @back="goBack">
<template #content>
<span class="page-title">{{ patientName }} - 护理评估</span>
<span class="page-title">{{ patientName }} - {{ $t('nursing.mobile.nursingAssessment') }}</span>
</template>
</el-page-header>
</div>
<div class="tool-selector">
<el-radio-group v-model="selectedTool" size="small" @change="onToolChange">
<el-radio-button value="BRADEN">Braden压疮</el-radio-button>
<el-radio-button value="MORSE">Morse跌倒</el-radio-button>
<el-radio-button value="NRS2002">NRS2002营养</el-radio-button>
<el-radio-button value="BRADEN">{{ $t('nursing.mobile.bradenPressure') }}</el-radio-button>
<el-radio-button value="MORSE">{{ $t('nursing.mobile.morseFall') }}</el-radio-button>
<el-radio-button value="NRS2002">{{ $t('nursing.mobile.nrs2002Nutrition') }}</el-radio-button>
</el-radio-group>
</div>
@@ -26,12 +26,8 @@
<div class="item-label">{{ item.label }}</div>
<div class="item-options">
<el-radio-group v-model="itemScores[item.key]" size="small">
<el-radio-button
v-for="opt in item.options"
:key="opt.value"
:value="opt.value"
>
{{ opt.label }} ({{ opt.value }})
<el-radio-button v-for="opt in item.options" :key="opt.value" :value="opt.value">
{{ opt.label }} ({{ opt.value }}{{ $t('nursing.mobile.scoreLabel') }})
</el-radio-button>
</el-radio-group>
</div>
@@ -40,42 +36,40 @@
<div class="score-summary">
<div class="total-score">
总分: <span class="score-value">{{ totalScore }}</span>
{{ $t('nursing.mobile.totalScoreLabel') }}: <span class="score-value">{{ totalScore }}</span>
</div>
<div class="risk-level">
风险等级:
{{ $t('nursing.mobile.riskLevelLabel') }}:
<el-tag :type="getRiskType(currentRiskLevel)" size="small">
{{ getRiskText(currentRiskLevel) }}
</el-tag>
</div>
</div>
<el-form-item label="评估备注" style="margin-top: 12px;">
<el-input v-model="detail" type="textarea" :rows="2" placeholder="可选备注" />
<el-form-item :label="$t('nursing.mobile.assessmentRemark')" style="margin-top: 12px;">
<el-input v-model="detail" type="textarea" :rows="2" :placeholder="$t('nursing.mobile.optionalRemark')" />
</el-form-item>
<el-button type="primary" style="width: 100%" @click="handleSubmit">
提交评估
{{ $t('nursing.mobile.submitAssessmentBtn') }}
</el-button>
</el-card>
<el-card class="history-section" v-loading="historyLoading">
<template #header>
<span>评估记录</span>
<span>{{ $t('nursing.mobile.assessmentRecordsLabel') }}</span>
</template>
<div v-for="record in historyList" :key="record.id" class="history-item">
<div class="history-header">
<span class="history-tool">{{ getToolName(record.assessmentTool) }}</span>
<el-tag :type="getRiskType(record.riskLevel)" size="small">
{{ getRiskText(record.riskLevel) }}
</el-tag>
<el-tag :type="getRiskType(record.riskLevel)" size="small">{{ getRiskText(record.riskLevel) }}</el-tag>
</div>
<div class="history-body">
<span class="history-score">评分: {{ record.totalScore }}</span>
<span class="history-score">{{ $t('nursing.mobile.scoreLabel') }}: {{ record.totalScore }}</span>
<span class="history-time">{{ formatTime(record.assessmentTime) }}</span>
</div>
</div>
<el-empty v-if="!historyLoading && historyList.length === 0" description="暂无评估记录" />
<el-empty v-if="!historyLoading && historyList.length === 0" :description="$t('nursing.mobile.noAssessmentRecords')" />
</el-card>
</div>
</template>
@@ -84,8 +78,10 @@
import { ref, reactive, computed, onMounted } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { ElMessage } from 'element-plus'
import { useI18n } from 'vue-i18n'
import { submitAssessment, getAssessmentList } from './api'
const { t } = useI18n()
const route = useRoute()
const router = useRouter()
const submitting = ref(false)
@@ -99,297 +95,158 @@ const selectedTool = ref('BRADEN')
const detail = ref('')
const historyList = ref([])
const bradenItems = [
{ key: 'sensory', label: '感觉感知', options: [
{ label: '完全受限', value: 1 }, { label: '严重受限', value: 2 },
{ label: '轻度受限', value: 3 }, { label: '未受损', value: 4 }
const bradenItems = computed(() => [
{ key: 'sensory', label: t('nursing.mobile.bradenSensory'), options: [
{ label: t('nursing.mobile.sensoryComplete'), value: 1 }, { label: t('nursing.mobile.sensorySevere'), value: 2 },
{ label: t('nursing.mobile.sensoryMild'), value: 3 }, { label: t('nursing.mobile.sensoryNone'), value: 4 }
]},
{ key: 'moisture', label: '潮湿程度', options: [
{ label: '持续潮湿', value: 1 }, { label: '经常潮湿', value: 2 },
{ label: '有时潮湿', value: 3 }, { label: '很少潮湿', value: 4 }
{ key: 'moisture', label: t('nursing.mobile.bradenMoisture'), options: [
{ label: t('nursing.mobile.moisturePersistent'), value: 1 }, { label: t('nursing.mobile.moistureFrequent'), value: 2 },
{ label: t('nursing.mobile.moistureOccasional'), value: 3 }, { label: t('nursing.mobile.moistureRare'), value: 4 }
]},
{ key: 'activity', label: '活动能力', options: [
{ label: '卧床不起', value: 1 }, { label: '仅限于椅', value: 2 },
{ label: '偶尔步行', value: 3 }, { label: '经常步行', value: 4 }
{ key: 'activity', label: t('nursing.mobile.bradenActivity'), options: [
{ label: t('nursing.mobile.activityBed'), value: 1 }, { label: t('nursing.mobile.activityChair'), value: 2 },
{ label: t('nursing.mobile.activityOccasionalWalk'), value: 3 }, { label: t('nursing.mobile.activityFrequentWalk'), value: 4 }
]},
{ key: 'mobility', label: '移动能力', options: [
{ label: '完全不动', value: 1 }, { label: '严重受限', value: 2 },
{ label: '轻度受限', value: 3 }, { label: '不受限', value: 4 }
{ key: 'mobility', label: t('nursing.mobile.bradenMobility'), options: [
{ label: t('nursing.mobile.mobilityNone'), value: 1 }, { label: t('nursing.mobile.mobilitySevere'), value: 2 },
{ label: t('nursing.mobile.mobilityMild'), value: 3 }, { label: t('nursing.mobile.mobilityUnlimited'), value: 4 }
]},
{ key: 'nutrition', label: '营养摄取', options: [
{ label: '非常差', value: 1 }, { label: '可能不足', value: 2 },
{ label: '充足', value: 3 }, { label: '丰富', value: 4 }
{ key: 'nutrition', label: t('nursing.mobile.bradenNutrition'), options: [
{ label: t('nursing.mobile.nutritionVeryPoor'), value: 1 }, { label: t('nursing.mobile.nutritionProbablyInsufficient'), value: 2 },
{ label: t('nursing.mobile.nutritionAdequate'), value: 3 }, { label: t('nursing.mobile.nutritionRich'), value: 4 }
]},
{ key: 'friction', label: '摩擦力和剪切力', options: [
{ label: '存在问题', value: 1 }, { label: '有潜在问题', value: 2 },
{ label: '无明显问题', value: 3 }
{ key: 'friction', label: t('nursing.mobile.bradenFriction'), options: [
{ label: t('nursing.mobile.frictionProblem'), value: 1 }, { label: t('nursing.mobile.frictionPotential'), value: 2 },
{ label: t('nursing.mobile.frictionNone'), value: 3 }
]}
]
])
const morseItems = [
{ key: 'fallHistory', label: '跌倒史', options: [
{ label: '无', value: 0 }, { label: '有', value: 25 }
const morseItems = computed(() => [
{ key: 'fallHistory', label: t('nursing.mobile.morseFallHistory'), options: [
{ label: t('nursing.mobile.no'), value: 0 }, { label: t('nursing.mobile.yes'), value: 25 }
]},
{ key: 'diagnosis', label: '超过一个医学诊断', options: [
{ label: '无', value: 0 }, { label: '有', value: 15 }
{ key: 'diagnosis', label: t('nursing.mobile.morseDiagnosis'), options: [
{ label: t('nursing.mobile.no'), value: 0 }, { label: t('nursing.mobile.yes'), value: 15 }
]},
{ key: 'ambulation', label: '行走辅助', options: [
{ label: '卧床/轮椅', value: 0 }, { label: '拐杖/助行器', value: 15 },
{ label: '扶家具', value: 30 }
{ key: 'ambulation', label: t('nursing.mobile.morseAmbulation'), options: [
{ label: t('nursing.mobile.ambulationBedWheelchair'), value: 0 }, { label: t('nursing.mobile.ambulationCrutch'), value: 15 },
{ label: t('nursing.mobile.ambulationFurniture'), value: 30 }
]},
{ key: 'ivTherapy', label: '静脉输液', options: [
{ label: '无', value: 0 }, { label: '有', value: 20 }
{ key: 'ivTherapy', label: t('nursing.mobile.morseIV'), options: [
{ label: t('nursing.mobile.no'), value: 0 }, { label: t('nursing.mobile.yes'), value: 20 }
]},
{ key: 'gait', label: '步态', options: [
{ label: '正常/卧床/轮椅', value: 0 }, { label: '虚弱', value: 10 },
{ label: '受损', value: 20 }
{ key: 'gait', label: t('nursing.mobile.morseGait'), options: [
{ label: t('nursing.mobile.gaitNormal'), value: 0 }, { label: t('nursing.mobile.gaitWeak'), value: 10 },
{ label: t('nursing.mobile.gaitImpaired'), value: 20 }
]},
{ key: 'mental', label: '精神状态', options: [
{ label: '正确评估自身能力', value: 0 }, { label: '高估或忘记限制', value: 15 }
{ key: 'mental', label: t('nursing.mobile.morseMental'), options: [
{ label: t('nursing.mobile.mentalCorrect'), value: 0 }, { label: t('nursing.mobile.mentalOverestimate'), value: 15 }
]}
]
])
const nrs2002Items = [
{ key: 'bmi', label: 'BMI(kg/m²)', options: [
{ label: '<18.5', value: 3 }, { label: '18.5-20.5', value: 1 },
{ label: '>20.5', value: 0 }
const nrs2002Items = computed(() => [
{ key: 'bmi', label: t('nursing.mobile.nrs2002BMI'), options: [
{ label: '<18.5', value: 3 }, { label: '18.5-20.5', value: 1 }, { label: '>20.5', value: 0 }
]},
{ key: 'weightLoss', label: '体重下降', options: [
{ label: '>10%/月', value: 3 }, { label: '5-10%/月', value: 2 },
{ label: '2-5%/月', value: 1 }, { label: '无/1月', value: 0 }
{ key: 'weightLoss', label: t('nursing.mobile.nrs2002WeightLoss'), options: [
{ label: '>10%/月', value: 3 }, { label: '5-10%/月', value: 2 }, { label: '2-5%/月', value: 1 }, { label: t('nursing.mobile.no') + '/1月', value: 0 }
]},
{ key: 'intake', label: '饮食摄入', options: [
{ label: '无', value: 3 }, { label: '差', value: 2 },
{ label: '中等', value: 1 }, { label: '好', value: 0 }
{ key: 'intake', label: t('nursing.mobile.nrs2002Intake'), options: [
{ label: t('nursing.mobile.intakeNone'), value: 3 }, { label: t('nursing.mobile.intakePoor'), value: 2 },
{ label: t('nursing.mobile.intakeModerate'), value: 1 }, { label: t('nursing.mobile.intakeGood'), value: 0 }
]},
{ key: 'illness', label: '疾病严重程度', options: [
{ label: '大手术/创伤', value: 3 }, { label: '骨盆骨折', value: 2 },
{ label: '慢性病急性发作', value: 1 }, { label: '无/轻度', value: 0 }
{ key: 'illness', label: t('nursing.mobile.nrs2002Illness'), options: [
{ label: t('nursing.mobile.illnessSurgery'), value: 3 }, { label: t('nursing.mobile.illnessFracture'), value: 2 },
{ label: t('nursing.mobile.illnessAcute'), value: 1 }, { label: t('nursing.mobile.illnessMild'), value: 0 }
]}
]
])
const toolConfigs = {
BRADEN: { title: 'Braden压疮风险评估', items: bradenItems },
MORSE: { title: 'Morse跌倒风险评估', items: morseItems },
NRS2002: { title: 'NRS2002营养风险筛查', items: nrs2002Items }
}
const toolConfigs = computed(() => ({
BRADEN: { title: 'Braden ' + t('nursing.mobile.bradenPressure'), items: bradenItems.value },
MORSE: { title: 'Morse ' + t('nursing.mobile.morseFall'), items: morseItems.value },
NRS2002: { title: 'NRS2002 ' + t('nursing.mobile.nrs2002Nutrition'), items: nrs2002Items.value }
}))
const itemScores = reactive({})
const toolConfig = computed(() => toolConfigs[selectedTool.value])
const toolConfig = computed(() => toolConfigs.value[selectedTool.value])
const totalScore = computed(() => {
let sum = 0
for (const item of toolConfig.value.items) {
sum += itemScores[item.key] || 0
}
for (const item of toolConfig.value.items) { sum += itemScores[item.key] || 0 }
return sum
})
const currentRiskLevel = computed(() => {
const tool = selectedTool.value
const score = totalScore.value
if (tool === 'BRADEN') {
if (score <= 12) return 'HIGH'
if (score <= 14) return 'MEDIUM'
return 'LOW'
} else if (tool === 'MORSE') {
if (score >= 45) return 'HIGH'
if (score >= 25) return 'MEDIUM'
return 'LOW'
} else if (tool === 'NRS2002') {
if (score >= 3) return 'HIGH'
return 'LOW'
}
const tool = selectedTool.value; const score = totalScore.value
if (tool === 'BRADEN') { if (score <= 12) return 'HIGH'; if (score <= 14) return 'MEDIUM'; return 'LOW' }
else if (tool === 'MORSE') { if (score >= 45) return 'HIGH'; if (score >= 25) return 'MEDIUM'; return 'LOW' }
else if (tool === 'NRS2002') { if (score >= 3) return 'HIGH'; return 'LOW' }
return 'NORMAL'
})
const onToolChange = () => {
for (const key of Object.keys(itemScores)) {
delete itemScores[key]
}
}
const onToolChange = () => { for (const key of Object.keys(itemScores)) { delete itemScores[key] } }
const getRiskType = (level) => {
const map = { HIGH: 'danger', MEDIUM: 'warning', LOW: 'success', NORMAL: 'info' }
return map[level] || 'info'
}
const getRiskType = (level) => ({ HIGH: 'danger', MEDIUM: 'warning', LOW: 'success', NORMAL: 'info' })[level] || 'info'
const getRiskText = (level) => ({
HIGH: t('nursing.mobile.highRisk'), MEDIUM: t('nursing.mobile.mediumRisk'),
LOW: t('nursing.mobile.lowRisk'), NORMAL: t('nursing.mobile.normal')
})[level] || t('nursing.mobile.unknown')
const getRiskText = (level) => {
const map = { HIGH: '高风险', MEDIUM: '中风险', LOW: '低风险', NORMAL: '正常' }
return map[level] || '未知'
}
const getToolName = (tool) => ({
BRADEN: t('nursing.mobile.bradenPressure'), MORSE: t('nursing.mobile.morseFall'), NRS2002: t('nursing.mobile.nrs2002Nutrition')
})[tool] || tool
const getToolName = (tool) => {
const map = { BRADEN: 'Braden压疮', MORSE: 'Morse跌倒', NRS2002: 'NRS2002营养' }
return map[tool] || tool
}
const formatTime = (time) => {
if (!time) return '-'
const d = new Date(time)
return d.toLocaleString('zh-CN')
}
const goBack = () => {
router.push('/nursingmobile/patient-list')
}
const formatTime = (time) => { if (!time) return '-'; const d = new Date(time); return d.toLocaleString() }
const goBack = () => { router.push('/nursingmobile/patient-list') }
const fetchHistory = async () => {
if (!patientId.value) return
historyLoading.value = true
try {
const res = await getAssessmentList(patientId.value)
historyList.value = (res.data || []).slice(0, 20)
} finally {
historyLoading.value = false
}
try { const res = await getAssessmentList(patientId.value); historyList.value = (res.data || []).slice(0, 20) } finally { historyLoading.value = false }
}
const handleSubmit = async () => {
const filled = toolConfig.value.items.filter(i => itemScores[i.key] != null)
if (filled.length < toolConfig.value.items.length) {
ElMessage.warning('请完成所有评估项目')
return
}
if (filled.length < toolConfig.value.items.length) { ElMessage.warning(t('nursing.mobile.completeAssessment')); return }
submitting.value = true
try {
await submitAssessment({
patientId: patientId.value ? Number(patientId.value) : null,
encounterId: encounterId.value ? Number(encounterId.value) : null,
patientName: patientName.value,
assessmentType: 'MOBILE',
assessmentTool: selectedTool.value,
totalScore: totalScore.value,
itemScores: { ...itemScores },
detail: detail.value,
assessmentTime: new Date()
patientName: patientName.value, assessmentType: 'MOBILE',
assessmentTool: selectedTool.value, totalScore: totalScore.value,
itemScores: { ...itemScores }, detail: detail.value, assessmentTime: new Date()
})
ElMessage.success('评估提交成功')
ElMessage.success(t('nursing.mobile.assessmentSubmitSuccess'))
detail.value = ''
fetchHistory()
} catch (e) {
ElMessage.error('提交失败')
} finally {
submitting.value = false
}
} catch (e) { ElMessage.error(t('nursing.mobile.assessmentSubmitFailed')) } finally { submitting.value = false }
}
onMounted(() => {
fetchHistory()
})
onMounted(() => { fetchHistory() })
</script>
<style scoped>
.mobile-assessment {
padding: 16px;
}
.page-header {
margin-bottom: 16px;
}
.page-title {
font-size: 16px;
font-weight: 600;
}
.tool-selector {
margin-bottom: 16px;
}
.assessment-form {
margin-bottom: 16px;
}
.items-list {
display: flex;
flex-direction: column;
gap: 16px;
}
.assessment-item {
border-bottom: 1px solid #f0f0f0;
padding-bottom: 12px;
}
.item-label {
font-size: 14px;
font-weight: 500;
margin-bottom: 8px;
color: #333;
}
.item-options {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.score-summary {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px;
background: #f5f7fa;
border-radius: 6px;
margin-top: 16px;
}
.total-score {
font-size: 15px;
font-weight: 500;
}
.score-value {
font-size: 20px;
font-weight: 700;
color: #409eff;
margin-left: 4px;
}
.risk-level {
font-size: 14px;
}
.history-section {
margin-bottom: 16px;
}
.history-item {
padding: 12px 0;
border-bottom: 1px solid #f0f0f0;
}
.history-item:last-child {
border-bottom: none;
}
.history-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 4px;
}
.history-tool {
font-size: 14px;
font-weight: 500;
}
.history-body {
display: flex;
justify-content: space-between;
align-items: center;
}
.history-score {
font-size: 13px;
color: #666;
}
.history-time {
font-size: 12px;
color: #999;
}
.mobile-assessment { padding: 16px; }
.page-header { margin-bottom: 16px; }
.page-title { font-size: 16px; font-weight: 600; }
.tool-selector { margin-bottom: 16px; }
.assessment-form { margin-bottom: 16px; }
.items-list { display: flex; flex-direction: column; gap: 16px; }
.assessment-item { border-bottom: 1px solid #f0f0f0; padding-bottom: 12px; }
.item-label { font-size: 14px; font-weight: 500; margin-bottom: 8px; color: #333; }
.item-options { display: flex; flex-wrap: wrap; gap: 8px; }
.score-summary { display: flex; justify-content: space-between; align-items: center; padding: 12px; background: #f5f7fa; border-radius: 6px; margin-top: 16px; }
.total-score { font-size: 15px; font-weight: 500; }
.score-value { font-size: 20px; font-weight: 700; color: #409eff; margin-left: 4px; }
.risk-level { font-size: 14px; }
.history-section { margin-bottom: 16px; }
.history-item { padding: 12px 0; border-bottom: 1px solid #f0f0f0; }
.history-item:last-child { border-bottom: none; }
.history-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 4px; }
.history-tool { font-size: 14px; font-weight: 500; }
.history-body { display: flex; justify-content: space-between; align-items: center; }
.history-score { font-size: 13px; color: #666; }
.history-time { font-size: 12px; color: #999; }
</style>

View File

@@ -10,69 +10,56 @@
<div class="filter-bar">
<el-radio-group v-model="statusFilter" size="small" @change="fetchOrders">
<el-radio-button :value="2">执行中</el-radio-button>
<el-radio-button :value="10">已校对</el-radio-button>
<el-radio-button :value="null">全部</el-radio-button>
<el-radio-button :value="2">{{ $t('nursing.mobile.orderExecuting') }}</el-radio-button>
<el-radio-button :value="10">{{ $t('nursing.mobile.orderProofread') }}</el-radio-button>
<el-radio-button :value="null">{{ $t('nursing.mobile.all') }}</el-radio-button>
</el-radio-group>
<el-button type="primary" size="small" @click="handleScan">
扫码执行
</el-button>
<el-button type="primary" size="small" @click="handleScan">{{ $t('nursing.mobile.scanExecute') }}</el-button>
</div>
<div v-loading="loading" class="order-list">
<div
v-for="order in orderList"
:key="order.requestId"
class="order-item"
>
<div v-for="order in orderList" :key="order.requestId" class="order-item">
<div class="order-header">
<span class="order-name">{{ order.adviceName }}</span>
<el-tag :type="getStatusType(order.requestStatus)" size="small">
{{ order.requestStatusText }}
</el-tag>
<el-tag :type="getStatusType(order.requestStatus)" size="small">{{ order.requestStatusText }}</el-tag>
</div>
<div class="order-body">
<div class="info-row">
<span class="label">类型:</span>
<span class="label">{{ $t('nursing.mobile.type') }}:</span>
<el-tag size="small">{{ order.therapyEnumText }}</el-tag>
</div>
<div v-if="order.frequencyUsage" class="info-row">
<span class="label">频次:</span>
<span class="label">{{ $t('nursing.mobile.frequency') }}:</span>
<span class="value">{{ order.frequencyUsage }}</span>
</div>
<div v-if="order.singleDose" class="info-row">
<span class="label">剂量:</span>
<span class="label">{{ $t('nursing.mobile.dosage') }}:</span>
<span class="value">{{ order.singleDose }}</span>
</div>
<div class="info-row">
<span class="label">开嘱医生:</span>
<span class="label">{{ $t('nursing.mobile.orderDoctor') }}:</span>
<span class="value">{{ order.requesterName }}</span>
</div>
</div>
<div class="order-footer">
<el-button
v-if="order.requestStatus === 2 || order.requestStatus === 10"
type="success"
size="small"
@click="handleExecute(order)"
>
执行
<el-button v-if="order.requestStatus === 2 || order.requestStatus === 10" type="success" size="small" @click="handleExecute(order)">
{{ $t('nursing.mobile.execute') }}
</el-button>
</div>
</div>
<el-empty v-if="!loading && orderList.length === 0" description="暂无医嘱" />
<el-empty v-if="!loading && orderList.length === 0" :description="$t('nursing.mobile.noOrders')" />
</div>
<el-dialog v-model="scanDialogVisible" title="扫码执行" width="400px">
<el-dialog v-model="scanDialogVisible" :title="$t('nursing.mobile.scanExecuteTitle')" width="400px">
<el-form :model="scanForm" label-width="80px">
<el-form-item label="条码">
<el-input v-model="scanForm.barcode" placeholder="请扫描或输入条码" />
<el-form-item :label="$t('nursing.mobile.barcode')">
<el-input v-model="scanForm.barcode" :placeholder="$t('nursing.mobile.barcodePlaceholder')" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="scanDialogVisible = false">取消</el-button>
<el-button type="primary" @click="confirmScan">确认执行</el-button>
<el-button @click="scanDialogVisible = false">{{ $t('common.cancel') }}</el-button>
<el-button type="primary" @click="confirmScan">{{ $t('nursing.mobile.confirmExecute') }}</el-button>
</template>
</el-dialog>
</div>
@@ -82,8 +69,10 @@
import { ref, onMounted } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { ElMessage, ElMessageBox } from 'element-plus'
import { useI18n } from 'vue-i18n'
import { getMobileOrderList, executeOrder } from './api'
const { t } = useI18n()
const route = useRoute()
const router = useRouter()
const loading = ref(false)
@@ -107,139 +96,52 @@ const fetchOrders = async () => {
}
}
const goBack = () => {
router.push('/nursingmobile/patient-list')
}
const goBack = () => { router.push('/nursingmobile/patient-list') }
const handleScan = () => {
scanForm.value.barcode = ''
scanDialogVisible.value = true
}
const handleScan = () => { scanForm.value.barcode = ''; scanDialogVisible.value = true }
const confirmScan = () => {
if (!scanForm.value.barcode) {
ElMessage.warning('请输入条码')
return
}
if (!scanForm.value.barcode) { ElMessage.warning(t('nursing.assessment.valBarcode')); return }
const matchedOrder = orderList.value.find(o => o.barcode === scanForm.value.barcode)
if (matchedOrder) {
handleExecute(matchedOrder)
} else {
ElMessage.error('未找到匹配的医嘱')
}
if (matchedOrder) { handleExecute(matchedOrder) } else { ElMessage.error(t('nursing.mobile.orderMatchFailed')) }
scanDialogVisible.value = false
}
const handleExecute = async (order) => {
try {
await ElMessageBox.confirm(
`确认执行医嘱: ${order.adviceName}?`,
'确认执行',
{ confirmButtonText: '确认', cancelButtonText: '取消', type: 'warning' }
`${t('nursing.mobile.confirmExecuteOrder')}: ${order.adviceName}?`,
t('nursing.mobile.confirmExecute'),
{ confirmButtonText: t('common.confirm'), cancelButtonText: t('common.cancel'), type: 'warning' }
)
loading.value = true
await executeOrder({
requestId: order.requestId,
adviceTable: order.adviceTable,
encounterId: order.encounterId,
patientId: order.patientId
})
ElMessage.success('执行成功')
await executeOrder({ requestId: order.requestId, adviceTable: order.adviceTable, encounterId: order.encounterId, patientId: order.patientId })
ElMessage.success(t('nursing.mobile.executeSuccess'))
fetchOrders()
} catch (e) {
if (e !== 'cancel') {
ElMessage.error('执行失败')
}
if (e !== 'cancel') { ElMessage.error(t('nursing.mobile.executeFailed')) }
} finally {
loading.value = false
}
}
const getStatusType = (status) => {
const map = { 2: 'primary', 3: 'success', 6: 'info', 10: 'warning', 11: '' }
return map[status] || 'info'
}
const getStatusType = (status) => ({ 2: 'primary', 3: 'success', 6: 'info', 10: 'warning', 11: '' })[status] || 'info'
onMounted(() => {
fetchOrders()
})
onMounted(() => { fetchOrders() })
</script>
<style scoped>
.mobile-order-list {
padding: 16px;
}
.page-header {
margin-bottom: 16px;
}
.page-title {
font-size: 16px;
font-weight: 600;
}
.filter-bar {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
}
.order-list {
display: flex;
flex-direction: column;
gap: 12px;
}
.order-item {
background: #fff;
border-radius: 8px;
padding: 16px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.order-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
}
.order-name {
font-size: 15px;
font-weight: 600;
flex: 1;
margin-right: 8px;
}
.order-body {
display: flex;
flex-direction: column;
gap: 8px;
}
.info-row {
display: flex;
align-items: center;
gap: 8px;
}
.info-row .label {
color: #666;
font-size: 13px;
min-width: 70px;
}
.info-row .value {
font-size: 14px;
}
.order-footer {
margin-top: 12px;
padding-top: 12px;
border-top: 1px solid #eee;
display: flex;
justify-content: flex-end;
}
.mobile-order-list { padding: 16px; }
.page-header { margin-bottom: 16px; }
.page-title { font-size: 16px; font-weight: 600; }
.filter-bar { display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px; }
.order-list { display: flex; flex-direction: column; gap: 12px; }
.order-item { background: #fff; border-radius: 8px; padding: 16px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); }
.order-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px; }
.order-name { font-size: 15px; font-weight: 600; flex: 1; margin-right: 8px; }
.order-body { display: flex; flex-direction: column; gap: 8px; }
.info-row { display: flex; align-items: center; gap: 8px; }
.info-row .label { color: #666; font-size: 13px; min-width: 70px; }
.info-row .value { font-size: 14px; }
.order-footer { margin-top: 12px; padding-top: 12px; border-top: 1px solid #eee; display: flex; justify-content: flex-end; }
</style>

View File

@@ -1,48 +1,31 @@
<template>
<div class="mobile-patient-list">
<div class="page-header">
<h2>患者列表</h2>
<el-input
v-model="searchKey"
placeholder="搜索姓名/床号"
clearable
style="width: 200px"
@input="handleSearch"
/>
<h2>{{ $t('nursing.mobile.patientList') }}</h2>
<el-input v-model="searchKey" :placeholder="$t('nursing.mobile.searchPlaceholder')" clearable style="width: 200px" @input="handleSearch" />
</div>
<div v-loading="loading" class="patient-cards">
<div
v-for="patient in patientList"
:key="patient.encounterId"
class="patient-card"
@click="handlePatientClick(patient)"
>
<div v-for="patient in patientList" :key="patient.encounterId" class="patient-card" @click="handlePatientClick(patient)">
<div class="card-header">
<span class="patient-name">{{ patient.patientName }}</span>
<el-tag :type="getGenderType(patient.genderEnum)" size="small">
{{ patient.genderEnumText }}
</el-tag>
<el-tag :type="getGenderType(patient.genderEnum)" size="small">{{ patient.genderEnumText }}</el-tag>
</div>
<div class="card-body">
<div class="info-row">
<span class="label">床号:</span>
<span class="label">{{ $t('nursing.mobile.bedNo') }}:</span>
<span class="value">{{ patient.bedName || '-' }}</span>
</div>
<div class="info-row">
<span class="label">护理等级:</span>
<el-tag :type="getNursingLevelType(patient.nursingLevel)" size="small">
{{ patient.nursingLevelText }}
</el-tag>
<span class="label">{{ $t('nursing.mobile.nursingLevel') }}:</span>
<el-tag :type="getNursingLevelType(patient.nursingLevel)" size="small">{{ patient.nursingLevelText }}</el-tag>
</div>
<div class="info-row">
<span class="label">病情:</span>
<el-tag :type="getPriorityType(patient.priorityEnum)" size="small">
{{ patient.priorityEnumText }}
</el-tag>
<span class="label">{{ $t('nursing.mobile.condition') }}:</span>
<el-tag :type="getPriorityType(patient.priorityEnum)" size="small">{{ patient.priorityEnumText }}</el-tag>
</div>
<div v-if="patient.diagnosis" class="info-row">
<span class="label">诊断:</span>
<span class="label">{{ $t('nursing.mobile.diagnosis') }}:</span>
<span class="value diagnosis">{{ patient.diagnosis }}</span>
</div>
</div>
@@ -51,7 +34,7 @@
</div>
</div>
<el-empty v-if="!loading && patientList.length === 0" description="暂无患者" />
<el-empty v-if="!loading && patientList.length === 0" :description="$t('nursing.mobile.noPatients')" />
</div>
</div>
</template>
@@ -76,126 +59,36 @@ const fetchData = async () => {
}
}
const handleSearch = () => {
fetchData()
}
const handleSearch = () => { fetchData() }
const handlePatientClick = (patient) => {
router.push({
path: '/nursingmobile/order-list',
query: {
encounterId: patient.encounterId,
patientId: patient.patientId,
patientName: patient.patientName,
bedName: patient.bedName
}
query: { encounterId: patient.encounterId, patientId: patient.patientId, patientName: patient.patientName, bedName: patient.bedName }
})
}
const getGenderType = (gender) => {
return gender === 1 ? 'primary' : gender === 2 ? 'danger' : 'info'
}
const getGenderType = (gender) => gender === 1 ? 'primary' : gender === 2 ? 'danger' : 'info'
const getNursingLevelType = (level) => ({ 1: 'danger', 2: 'warning', 3: '', 4: 'danger' })[level] || 'info'
const getPriorityType = (priority) => ({ 1: 'danger', 2: 'warning', 3: '', 4: 'info' })[priority] || 'info'
const getNursingLevelType = (level) => {
const map = { 1: 'danger', 2: 'warning', 3: '', 4: 'danger' }
return map[level] || 'info'
}
const getPriorityType = (priority) => {
const map = { 1: 'danger', 2: 'warning', 3: '', 4: 'info' }
return map[priority] || 'info'
}
onMounted(() => {
fetchData()
})
onMounted(() => { fetchData() })
</script>
<style scoped>
.mobile-patient-list {
padding: 16px;
}
.page-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
}
.page-header h2 {
margin: 0;
font-size: 18px;
}
.patient-cards {
display: flex;
flex-direction: column;
gap: 12px;
}
.patient-card {
background: #fff;
border-radius: 8px;
padding: 16px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
cursor: pointer;
transition: transform 0.2s;
}
.patient-card:active {
transform: scale(0.98);
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
}
.patient-name {
font-size: 16px;
font-weight: 600;
}
.card-body {
display: flex;
flex-direction: column;
gap: 8px;
}
.info-row {
display: flex;
align-items: center;
gap: 8px;
}
.info-row .label {
color: #666;
font-size: 13px;
min-width: 60px;
}
.info-row .value {
font-size: 14px;
}
.info-row .diagnosis {
color: #e6a23c;
}
.card-footer {
margin-top: 12px;
padding-top: 12px;
border-top: 1px solid #eee;
display: flex;
justify-content: space-between;
align-items: center;
}
.doctor {
color: #999;
font-size: 12px;
}
.mobile-patient-list { padding: 16px; }
.page-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px; }
.page-header h2 { margin: 0; font-size: 18px; }
.patient-cards { display: flex; flex-direction: column; gap: 12px; }
.patient-card { background: #fff; border-radius: 8px; padding: 16px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); cursor: pointer; transition: transform 0.2s; }
.patient-card:active { transform: scale(0.98); }
.card-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px; }
.patient-name { font-size: 16px; font-weight: 600; }
.card-body { display: flex; flex-direction: column; gap: 8px; }
.info-row { display: flex; align-items: center; gap: 8px; }
.info-row .label { color: #666; font-size: 13px; min-width: 60px; }
.info-row .value { font-size: 14px; }
.info-row .diagnosis { color: #e6a23c; }
.card-footer { margin-top: 12px; padding-top: 12px; border-top: 1px solid #eee; display: flex; justify-content: space-between; align-items: center; }
.doctor { color: #999; font-size: 12px; }
</style>

View File

@@ -3,92 +3,79 @@
<div class="page-header">
<el-page-header @back="goBack">
<template #content>
<span class="page-title">生命体征录入</span>
<span class="page-title">{{ $t('nursing.mobile.vitalSignEntry') }}</span>
</template>
</el-page-header>
</div>
<el-form
ref="formRef"
:model="form"
:rules="rules"
label-width="80px"
class="vital-form"
>
<el-form-item label="患者">
<el-form ref="formRef" :model="form" :rules="rules" label-width="80px" class="vital-form">
<el-form-item :label="$t('nursing.mobile.patient')">
<span class="patient-info">{{ patientName }} ({{ bedName }})</span>
</el-form-item>
<el-form-item label="记录时间">
<el-date-picker
v-model="form.recordDate"
type="date"
placeholder="选择日期"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
style="width: 100%"
/>
<el-form-item :label="$t('nursing.mobile.recordDate')">
<el-date-picker v-model="form.recordDate" type="date" :placeholder="$t('nursing.mobile.selectDate')" format="YYYY-MM-DD" value-format="YYYY-MM-DD" style="width: 100%" />
</el-form-item>
<el-form-item label="时点">
<el-select v-model="form.recordHour" placeholder="选择时点" style="width: 100%">
<el-form-item :label="$t('nursing.mobile.recordHour')">
<el-select v-model="form.recordHour" :placeholder="$t('nursing.mobile.recordHour')" style="width: 100%">
<el-option v-for="h in 24" :key="h-1" :label="(h-1)+':00'" :value="h-1" />
</el-select>
</el-form-item>
<el-form-item label="体温" prop="temperature">
<el-form-item :label="$t('nursing.mobile.temperature')" prop="temperature">
<el-input-number v-model="form.temperature" :min="35" :max="42" :step="0.1" :precision="1" style="width: 100%" />
<span class="unit">°C</span>
</el-form-item>
<el-form-item label="脉搏" prop="pulse">
<el-form-item :label="$t('nursing.mobile.pulse')" prop="pulse">
<el-input-number v-model="form.pulse" :min="40" :max="200" style="width: 100%" />
<span class="unit">/</span>
<span class="unit">{{ $t('nursing.mobile.unitTimesPerMin') }}</span>
</el-form-item>
<el-form-item label="呼吸" prop="respiration">
<el-form-item :label="$t('nursing.mobile.respiration')" prop="respiration">
<el-input-number v-model="form.respiration" :min="10" :max="60" style="width: 100%" />
<span class="unit">/</span>
<span class="unit">{{ $t('nursing.mobile.unitTimesPerMin') }}</span>
</el-form-item>
<el-form-item label="收缩压" prop="systolicBp">
<el-form-item :label="$t('nursing.mobile.systolicBp')" prop="systolicBp">
<el-input-number v-model="form.systolicBp" :min="60" :max="300" style="width: 100%" />
<span class="unit">mmHg</span>
</el-form-item>
<el-form-item label="舒张压" prop="diastolicBp">
<el-form-item :label="$t('nursing.mobile.diastolicBp')" prop="diastolicBp">
<el-input-number v-model="form.diastolicBp" :min="30" :max="200" style="width: 100%" />
<span class="unit">mmHg</span>
</el-form-item>
<el-form-item label="疼痛评分">
<el-form-item :label="$t('nursing.mobile.painScore')">
<el-rate v-model="form.painScore" :max="10" show-score />
</el-form-item>
<el-form-item label="意识">
<el-select v-model="form.consciousLevel" placeholder="选择意识状态" style="width: 100%">
<el-option label="清醒" value="清醒" />
<el-option label="嗜睡" value="嗜睡" />
<el-option label="模糊" value="模糊" />
<el-option label="昏睡" value="昏睡" />
<el-option label="浅昏迷" value="浅昏迷" />
<el-option label="深昏迷" value="深昏迷" />
<el-form-item :label="$t('nursing.mobile.consciousness')">
<el-select v-model="form.consciousLevel" :placeholder="$t('nursing.mobile.consciousness')" style="width: 100%">
<el-option :label="$t('nursing.mobile.consciousClear')" value="清醒" />
<el-option :label="$t('nursing.mobile.consciousDrowsy')" value="嗜睡" />
<el-option :label="$t('nursing.mobile.consciousConfused')" value="模糊" />
<el-option :label="$t('nursing.mobile.consciousStupor')" value="昏睡" />
<el-option :label="$t('nursing.mobile.consciousComaShallow')" value="浅昏迷" />
<el-option :label="$t('nursing.mobile.consciousComaDeep')" value="深昏迷" />
</el-select>
</el-form-item>
<el-form-item label="入量">
<el-form-item :label="$t('nursing.mobile.inputVolume')">
<el-input-number v-model="form.inputMl" :min="0" style="width: 100%" />
<span class="unit">ml</span>
</el-form-item>
<el-form-item label="出量">
<el-form-item :label="$t('nursing.mobile.outputVolume')">
<el-input-number v-model="form.outputMl" :min="0" style="width: 100%" />
<span class="unit">ml</span>
</el-form-item>
<el-form-item>
<el-button type="primary" :loading="submitting" style="width: 100%" @click="handleSubmit">
保存
{{ $t('nursing.mobile.save') }}
</el-button>
</el-form-item>
</el-form>
@@ -96,11 +83,13 @@
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue'
import { ref, reactive, computed, onMounted } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { ElMessage } from 'element-plus'
import { useI18n } from 'vue-i18n'
import { saveVitalSign } from './api'
const { t } = useI18n()
const route = useRoute()
const router = useRouter()
const formRef = ref(null)
@@ -118,40 +107,30 @@ const form = reactive({
patientName: patientName.value,
recordDate: now.toISOString().split('T')[0],
recordHour: now.getHours(),
temperature: null,
pulse: null,
respiration: null,
systolicBp: null,
diastolicBp: null,
painScore: 0,
consciousLevel: '清醒',
inputMl: null,
outputMl: null,
nurseName: ''
temperature: null, pulse: null, respiration: null,
systolicBp: null, diastolicBp: null,
painScore: 0, consciousLevel: '清醒',
inputMl: null, outputMl: null, nurseName: ''
})
const rules = {
temperature: [{ required: true, message: '请输入体温', trigger: 'blur' }],
pulse: [{ required: true, message: '请输入脉搏', trigger: 'blur' }],
systolicBp: [{ required: true, message: '请输入收缩压', trigger: 'blur' }],
diastolicBp: [{ required: true, message: '请输入舒张压', trigger: 'blur' }]
}
const rules = computed(() => ({
temperature: [{ required: true, message: t('nursing.assessment.valTemperature'), trigger: 'blur' }],
pulse: [{ required: true, message: t('nursing.assessment.valPulse'), trigger: 'blur' }],
systolicBp: [{ required: true, message: t('nursing.assessment.valSystolicBp'), trigger: 'blur' }],
diastolicBp: [{ required: true, message: t('nursing.assessment.valDiastolicBp'), trigger: 'blur' }]
}))
const goBack = () => {
router.push('/nursingmobile/patient-list')
}
const goBack = () => { router.push('/nursingmobile/patient-list') }
const handleSubmit = async () => {
try {
await formRef.value.validate()
submitting.value = true
await saveVitalSign(form)
ElMessage.success('保存成功')
ElMessage.success(t('nursing.mobile.saveSuccess'))
router.push('/nursingmobile/vital-sign-trend?patientId=' + patientId.value + '&patientName=' + patientName.value)
} catch (e) {
if (e !== false) {
ElMessage.error('保存失败')
}
if (e !== false) { ElMessage.error(t('nursing.mobile.saveFailed')) }
} finally {
submitting.value = false
}
@@ -161,35 +140,10 @@ onMounted(() => {})
</script>
<style scoped>
.mobile-vital-sign {
padding: 16px;
}
.page-header {
margin-bottom: 16px;
}
.page-title {
font-size: 16px;
font-weight: 600;
}
.vital-form {
background: #fff;
border-radius: 8px;
padding: 16px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.patient-info {
font-size: 15px;
font-weight: 600;
color: #409eff;
}
.unit {
margin-left: 8px;
color: #999;
font-size: 12px;
}
.mobile-vital-sign { padding: 16px; }
.page-header { margin-bottom: 16px; }
.page-title { font-size: 16px; font-weight: 600; }
.vital-form { background: #fff; border-radius: 8px; padding: 16px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); }
.patient-info { font-size: 15px; font-weight: 600; color: #409eff; }
.unit { margin-left: 8px; color: #999; font-size: 12px; }
</style>

View File

@@ -3,28 +3,26 @@
<div class="page-header">
<el-page-header @back="goBack">
<template #content>
<span class="page-title">体征趋势</span>
<span class="page-title">{{ $t('nursing.mobile.vitalTrend') }}</span>
</template>
</el-page-header>
</div>
<div class="patient-info">
<span>{{ patientName }}</span>
</div>
<div class="patient-info"><span>{{ patientName }}</span></div>
<div class="days-filter">
<el-radio-group v-model="days" size="small" @change="fetchTrend">
<el-radio-button :value="3">3</el-radio-button>
<el-radio-button :value="7">7</el-radio-button>
<el-radio-button :value="14">14</el-radio-button>
<el-radio-button :value="3">{{ $t('nursing.mobile.days3') }}</el-radio-button>
<el-radio-button :value="7">{{ $t('nursing.mobile.days7') }}</el-radio-button>
<el-radio-button :value="14">{{ $t('nursing.mobile.days14') }}</el-radio-button>
</el-radio-group>
</div>
<div v-loading="loading" class="trend-charts">
<div class="chart-section">
<h4>体温 (°C)</h4>
<h4>{{ $t('nursing.mobile.temperatureChart') }}</h4>
<div class="chart-container">
<div v-if="trendData.temperatureData.length === 0" class="no-data">暂无数据</div>
<div v-if="trendData.temperatureData.length === 0" class="no-data">{{ $t('nursing.mobile.noData') }}</div>
<div v-else class="simple-chart">
<div v-for="(point, idx) in trendData.temperatureData" :key="idx" class="chart-point">
<div class="point-value">{{ point.value }}</div>
@@ -36,9 +34,9 @@
</div>
<div class="chart-section">
<h4>脉搏 (/)</h4>
<h4>{{ $t('nursing.mobile.pulseChart') }}</h4>
<div class="chart-container">
<div v-if="trendData.pulseData.length === 0" class="no-data">暂无数据</div>
<div v-if="trendData.pulseData.length === 0" class="no-data">{{ $t('nursing.mobile.noData') }}</div>
<div v-else class="simple-chart">
<div v-for="(point, idx) in trendData.pulseData" :key="idx" class="chart-point">
<div class="point-value">{{ point.value }}</div>
@@ -50,9 +48,9 @@
</div>
<div class="chart-section">
<h4>血压 (mmHg)</h4>
<h4>{{ $t('nursing.mobile.bpChart') }}</h4>
<div class="chart-container">
<div v-if="trendData.systolicBpData.length === 0" class="no-data">暂无数据</div>
<div v-if="trendData.systolicBpData.length === 0" class="no-data">{{ $t('nursing.mobile.noData') }}</div>
<div v-else class="simple-chart">
<div v-for="(point, idx) in trendData.systolicBpData" :key="idx" class="chart-point">
<div class="point-value">{{ point.value }}/{{ getDiastolicValue(idx) }}</div>
@@ -64,9 +62,9 @@
</div>
<div class="chart-section">
<h4>呼吸 (/)</h4>
<h4>{{ $t('nursing.mobile.respirationChart') }}</h4>
<div class="chart-container">
<div v-if="trendData.respirationData.length === 0" class="no-data">暂无数据</div>
<div v-if="trendData.respirationData.length === 0" class="no-data">{{ $t('nursing.mobile.noData') }}</div>
<div v-else class="simple-chart">
<div v-for="(point, idx) in trendData.respirationData" :key="idx" class="chart-point">
<div class="point-value">{{ point.value }}</div>
@@ -93,11 +91,7 @@ const patientName = ref(route.query.patientName || '')
const patientId = ref(route.query.patientId)
const trendData = ref({
temperatureData: [],
pulseData: [],
systolicBpData: [],
diastolicBpData: [],
respirationData: []
temperatureData: [], pulseData: [], systolicBpData: [], diastolicBpData: [], respirationData: []
})
const fetchTrend = async () => {
@@ -105,147 +99,38 @@ const fetchTrend = async () => {
loading.value = true
try {
const res = await getVitalSignTrend(patientId.value, { days: days.value })
trendData.value = res.data || {
temperatureData: [],
pulseData: [],
systolicBpData: [],
diastolicBpData: [],
respirationData: []
}
if (res.data?.patientName) {
patientName.value = res.data.patientName
}
trendData.value = res.data || { temperatureData: [], pulseData: [], systolicBpData: [], diastolicBpData: [], respirationData: [] }
if (res.data?.patientName) { patientName.value = res.data.patientName }
} finally {
loading.value = false
}
}
const goBack = () => {
router.push('/nursingmobile/patient-list')
}
const goBack = () => { router.push('/nursingmobile/patient-list') }
const getBarHeight = (value, min, max) => { if (!value) return 0; const normalized = (value - min) / (max - min); return Math.max(10, Math.min(80, normalized * 80)) }
const getDiastolicValue = (idx) => { const point = trendData.value.diastolicBpData[idx]; return point ? point.value : '-' }
const formatLabel = (label) => { if (!label) return ''; const parts = label.split(' '); return parts.length > 1 ? parts[1] : label }
const getBarHeight = (value, min, max) => {
if (!value) return 0
const normalized = (value - min) / (max - min)
return Math.max(10, Math.min(80, normalized * 80))
}
const getDiastolicValue = (idx) => {
const point = trendData.value.diastolicBpData[idx]
return point ? point.value : '-'
}
const formatLabel = (label) => {
if (!label) return ''
const parts = label.split(' ')
return parts.length > 1 ? parts[1] : label
}
onMounted(() => {
fetchTrend()
})
onMounted(() => { fetchTrend() })
</script>
<style scoped>
.mobile-vital-trend {
padding: 16px;
}
.page-header {
margin-bottom: 16px;
}
.page-title {
font-size: 16px;
font-weight: 600;
}
.patient-info {
font-size: 15px;
font-weight: 600;
color: #409eff;
margin-bottom: 16px;
}
.days-filter {
margin-bottom: 16px;
}
.trend-charts {
display: flex;
flex-direction: column;
gap: 20px;
}
.chart-section {
background: #fff;
border-radius: 8px;
padding: 16px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.chart-section h4 {
margin: 0 0 12px 0;
font-size: 14px;
color: #333;
}
.chart-container {
min-height: 100px;
}
.no-data {
text-align: center;
color: #999;
padding: 40px 0;
}
.simple-chart {
display: flex;
gap: 8px;
overflow-x: auto;
padding-bottom: 8px;
}
.chart-point {
display: flex;
flex-direction: column;
align-items: center;
min-width: 50px;
}
.point-value {
font-size: 11px;
font-weight: 600;
color: #333;
margin-bottom: 4px;
}
.point-bar {
width: 20px;
background: #409eff;
border-radius: 4px 4px 0 0;
min-height: 10px;
}
.point-bar.pulse {
background: #67c23a;
}
.point-bar.bp {
background: #e6a23c;
}
.point-bar.resp {
background: #909399;
}
.point-label {
font-size: 10px;
color: #999;
margin-top: 4px;
text-align: center;
writing-mode: vertical-rl;
max-height: 60px;
}
.mobile-vital-trend { padding: 16px; }
.page-header { margin-bottom: 16px; }
.page-title { font-size: 16px; font-weight: 600; }
.patient-info { font-size: 15px; font-weight: 600; color: #409eff; margin-bottom: 16px; }
.days-filter { margin-bottom: 16px; }
.trend-charts { display: flex; flex-direction: column; gap: 20px; }
.chart-section { background: #fff; border-radius: 8px; padding: 16px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); }
.chart-section h4 { margin: 0 0 12px 0; font-size: 14px; color: #333; }
.chart-container { min-height: 100px; }
.no-data { text-align: center; color: #999; padding: 40px 0; }
.simple-chart { display: flex; gap: 8px; overflow-x: auto; padding-bottom: 8px; }
.chart-point { display: flex; flex-direction: column; align-items: center; min-width: 50px; }
.point-value { font-size: 11px; font-weight: 600; color: #333; margin-bottom: 4px; }
.point-bar { width: 20px; background: #409eff; border-radius: 4px 4px 0 0; min-height: 10px; }
.point-bar.pulse { background: #67c23a; }
.point-bar.bp { background: #e6a23c; }
.point-bar.resp { background: #909399; }
.point-label { font-size: 10px; color: #999; margin-top: 4px; text-align: center; writing-mode: vertical-rl; max-height: 60px; }
</style>

View File

@@ -1,143 +1,61 @@
<template>
<div style="padding:16px">
<div style="margin-bottom:16px;display:flex;justify-content:space-between;align-items:center">
<span style="font-size:18px;font-weight:bold">护理质量指标管理</span>
<span style="font-size:18px;font-weight:bold">{{ $t('nursing.quality.title') }}</span>
<div>
<el-button
type="primary"
@click="loadData"
>
刷新
</el-button>
<el-button
type="success"
@click="showAdd = true"
>
新增指标
</el-button>
<el-button
type="warning"
@click="handleCollect"
>
采集指标
</el-button>
<el-button
type="info"
@click="exportReport"
>
导出报告
</el-button>
<el-button type="primary" @click="loadData">{{ $t('nursing.quality.refresh') }}</el-button>
<el-button type="success" @click="showAdd = true">{{ $t('nursing.quality.addIndicator') }}</el-button>
<el-button type="warning" @click="handleCollect">{{ $t('nursing.quality.collectIndicator') }}</el-button>
<el-button type="info" @click="exportReport">{{ $t('nursing.quality.exportReport') }}</el-button>
</div>
</div>
<!-- 统计卡片 -->
<el-row
:gutter="16"
style="margin-bottom:16px"
>
<el-col
v-for="item in statCards"
:key="item.label"
:span="4"
>
<el-card
shadow="hover"
:body-style="{padding:'10px'}"
>
<el-row :gutter="16" style="margin-bottom:16px">
<el-col v-for="item in statCards" :key="item.label" :span="4">
<el-card shadow="hover" :body-style="{padding:'10px'}">
<div style="text-align:center">
<div
style="font-size:20px;font-weight:bold"
:style="{color:item.color}"
>
{{ item.value }}
</div>
<div style="font-size:12px;color:#999">
{{ item.label }}
</div>
<div style="font-size:20px;font-weight:bold" :style="{color:item.color}">{{ item.value }}</div>
<div style="font-size:12px;color:#999">{{ item.label }}</div>
</div>
</el-card>
</el-col>
</el-row>
<!-- 图表展示区域 -->
<el-row
:gutter="16"
style="margin-bottom:16px"
>
<el-row :gutter="16" style="margin-bottom:16px">
<el-col :span="12">
<el-card shadow="never">
<template #header>达标率趋势</template>
<div
ref="trendChartRef"
style="height:300px"
/>
<template #header>{{ $t('nursing.quality.meetRateTrend') }}</template>
<div ref="trendChartRef" style="height:300px" />
</el-card>
</el-col>
<el-col :span="12">
<el-card shadow="never">
<template #header>科室达标对比</template>
<div
ref="deptChartRef"
style="height:300px"
/>
<template #header>{{ $t('nursing.quality.deptMeetCompare') }}</template>
<div ref="deptChartRef" style="height:300px" />
</el-card>
</el-col>
</el-row>
<el-row
:gutter="16"
style="margin-bottom:16px"
>
<el-row :gutter="16" style="margin-bottom:16px">
<el-col :span="8">
<el-card shadow="never">
<template #header>指标类别分布</template>
<div
ref="categoryChartRef"
style="height:300px"
/>
<template #header>{{ $t('nursing.quality.categoryDistribution') }}</template>
<div ref="categoryChartRef" style="height:300px" />
</el-card>
</el-col>
<el-col :span="16">
<el-card shadow="never">
<template #header>未达标指标预警</template>
<el-table
:data="unmetIndicators"
border
stripe
max-height="300"
>
<el-table-column
prop="indicatorName"
label="指标名称"
min-width="150"
show-overflow-tooltip
/>
<el-table-column
prop="departmentName"
label="科室"
width="100"
/>
<el-table-column
prop="targetValue"
label="目标值"
width="80"
align="center"
/>
<el-table-column
prop="actualValue"
label="实际值"
width="80"
align="center"
>
<template #header>{{ $t('nursing.quality.unmetWarning') }}</template>
<el-table :data="unmetIndicators" border stripe max-height="300">
<el-table-column prop="indicatorName" :label="$t('nursing.quality.indicatorName')" min-width="150" show-overflow-tooltip />
<el-table-column prop="departmentName" :label="$t('nursing.quality.department')" width="100" />
<el-table-column prop="targetValue" :label="$t('nursing.quality.targetValue')" width="80" align="center" />
<el-table-column prop="actualValue" :label="$t('nursing.quality.actualValue')" width="80" align="center">
<template #default="{row}">
<span style="color:#F56C6C;font-weight:bold">{{ row.actualValue }}</span>
</template>
</el-table-column>
<el-table-column
label="差距"
width="80"
align="center"
>
<el-table-column :label="$t('nursing.quality.gap')" width="80" align="center">
<template #default="{row}">
<span style="color:#F56C6C">{{ (row.actualValue - row.targetValue).toFixed(1) }}</span>
</template>
@@ -147,269 +65,90 @@
</el-col>
</el-row>
<!-- 筛选 -->
<div style="margin-bottom:12px;display:flex;gap:8px;flex-wrap:wrap">
<el-select
v-model="q.indicatorCategory"
placeholder="指标类别"
clearable
style="width:140px"
>
<el-option
label="基础护理"
value="BASIC"
/>
<el-option
label="专科护理"
value="SPECIALIZED"
/>
<el-option
label="护理安全"
value="SAFETY"
/>
<el-option
label="护理文书"
value="DOCUMENTATION"
/>
<el-option
label="消毒隔离"
value="STERILIZATION"
/>
<el-select v-model="q.indicatorCategory" :placeholder="$t('nursing.quality.indicatorCategory')" clearable style="width:140px">
<el-option :label="$t('nursing.quality.basicNursing')" value="BASIC" />
<el-option :label="$t('nursing.quality.specializedNursing')" value="SPECIALIZED" />
<el-option :label="$t('nursing.quality.nursingSafety')" value="SAFETY" />
<el-option :label="$t('nursing.quality.nursingDocumentation')" value="DOCUMENTATION" />
<el-option :label="$t('nursing.quality.sterilization')" value="STERILIZATION" />
</el-select>
<el-select
v-model="q.departmentName"
placeholder="科室"
clearable
style="width:140px"
>
<el-option
label="内科"
value="内科"
/>
<el-option
label="外科"
value="外科"
/>
<el-option
label="妇产科"
value="妇产科"
/>
<el-option
label="儿科"
value="儿科"
/>
<el-option
label="ICU"
value="ICU"
/>
<el-option
label="急诊科"
value="急诊科"
/>
<el-select v-model="q.departmentName" :placeholder="$t('nursing.quality.department')" clearable style="width:140px">
<el-option :label="$t('nursing.quality.internalMedicine')" value="内科" />
<el-option :label="$t('nursing.quality.surgery')" value="外科" />
<el-option :label="$t('nursing.quality.obstetrics')" value="妇产科" />
<el-option :label="$t('nursing.quality.pediatrics')" value="儿科" />
<el-option :label="$t('nursing.quality.icu')" value="ICU" />
<el-option :label="$t('nursing.quality.emergency')" value="急诊科" />
</el-select>
<el-button
type="primary"
@click="loadData"
>
查询
</el-button>
<el-button @click="resetQuery">
重置
</el-button>
<el-button type="primary" @click="loadData">{{ $t('nursing.quality.query') }}</el-button>
<el-button @click="resetQuery">{{ $t('nursing.quality.resetQuery') }}</el-button>
</div>
<!-- 数据表格 -->
<el-table
v-loading="loading"
:data="indicatorData"
border
stripe
>
<el-table-column
prop="indicatorCode"
label="指标编码"
width="120"
/>
<el-table-column
prop="indicatorName"
label="指标名称"
min-width="180"
show-overflow-tooltip
/>
<el-table-column
prop="indicatorCategory"
label="类别"
width="100"
align="center"
>
<el-table v-loading="loading" :data="indicatorData" border stripe>
<el-table-column prop="indicatorCode" :label="$t('nursing.quality.indicatorCode')" width="120" />
<el-table-column prop="indicatorName" :label="$t('nursing.quality.indicatorName')" min-width="180" show-overflow-tooltip />
<el-table-column prop="indicatorCategory" :label="$t('nursing.quality.category')" width="100" align="center">
<template #default="{row}">
<el-tag size="small">
{{ categoryText(row.indicatorCategory) }}
<el-tag size="small">{{ categoryText(row.indicatorCategory) }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="targetValue" :label="$t('nursing.quality.targetValue')" width="80" align="center" />
<el-table-column prop="actualValue" :label="$t('nursing.quality.actualValue')" width="80" align="center">
<template #default="{row}">
<span :style="{color: row.actualValue >= row.targetValue ? '#67C23A' : '#F56C6C', fontWeight:'bold'}">{{ row.actualValue }}</span>
</template>
</el-table-column>
<el-table-column :label="$t('nursing.quality.metStatus')" width="90" align="center">
<template #default="{row}">
<el-tag :type="row.actualValue >= row.targetValue ? 'success' : 'danger'" size="small">
{{ row.actualValue >= row.targetValue ? $t('nursing.quality.met') : $t('nursing.quality.unmet') }}
</el-tag>
</template>
</el-table-column>
<el-table-column
prop="targetValue"
label="目标值"
width="80"
align="center"
/>
<el-table-column
prop="actualValue"
label="实际值"
width="80"
align="center"
>
<template #default="{row}">
<span :style="{color: row.actualValue >= row.targetValue ? '#67C23A' : '#F56C6C', fontWeight:'bold'}">
{{ row.actualValue }}
</span>
</template>
</el-table-column>
<el-table-column
label="达标状态"
width="90"
align="center"
>
<template #default="{row}">
<el-tag
:type="row.actualValue >= row.targetValue ? 'success' : 'danger'"
size="small"
>
{{ row.actualValue >= row.targetValue ? '达标' : '未达标' }}
</el-tag>
</template>
</el-table-column>
<el-table-column
prop="departmentName"
label="科室"
width="100"
/>
<el-table-column
prop="statDate"
label="统计日期"
width="120"
/>
<el-table-column
prop="remark"
label="备注"
min-width="150"
show-overflow-tooltip
/>
<el-table-column prop="departmentName" :label="$t('nursing.quality.department')" width="100" />
<el-table-column prop="statDate" :label="$t('nursing.quality.statDate')" width="120" />
<el-table-column prop="remark" :label="$t('nursing.quality.remark')" min-width="150" show-overflow-tooltip />
</el-table>
<el-pagination
v-model:current-page="q.pageNo"
v-model:page-size="q.pageSize"
style="margin-top:12px;justify-content:flex-end"
:total="total"
layout="total, sizes, prev, pager, next, jumper"
@size-change="loadData"
@current-change="loadData"
/>
<el-pagination v-model:current-page="q.pageNo" v-model:page-size="q.pageSize" style="margin-top:12px;justify-content:flex-end" :total="total" layout="total, sizes, prev, pager, next, jumper" @size-change="loadData" @current-change="loadData" />
<!-- 新增弹窗 -->
<el-dialog
v-model="showAdd"
title="新增护理质量指标"
width="600px"
append-to-body
>
<el-form
:model="formData"
label-width="110px"
>
<el-form-item
label="指标编码"
required
>
<el-input
v-model="formData.indicatorCode"
placeholder="如: NQ001"
/>
<el-dialog v-model="showAdd" :title="$t('nursing.quality.addDialog')" width="600px" append-to-body>
<el-form :model="formData" label-width="110px">
<el-form-item :label="$t('nursing.quality.indicatorCode')" required>
<el-input v-model="formData.indicatorCode" :placeholder="$t('nursing.quality.indicatorCodePlaceholder')" />
</el-form-item>
<el-form-item
label="指标名称"
required
>
<el-input
v-model="formData.indicatorName"
placeholder="请输入指标名称"
/>
<el-form-item :label="$t('nursing.quality.indicatorName')" required>
<el-input v-model="formData.indicatorName" :placeholder="$t('nursing.quality.indicatorNamePlaceholder')" />
</el-form-item>
<el-form-item
label="指标类别"
required
>
<el-select
v-model="formData.indicatorCategory"
placeholder="请选择"
>
<el-option
label="基础护理"
value="BASIC"
/>
<el-option
label="专科护理"
value="SPECIALIZED"
/>
<el-option
label="护理安全"
value="SAFETY"
/>
<el-option
label="护理文书"
value="DOCUMENTATION"
/>
<el-option
label="消毒隔离"
value="STERILIZATION"
/>
<el-form-item :label="$t('nursing.quality.indicatorCategory')" required>
<el-select v-model="formData.indicatorCategory" :placeholder="$t('nursing.quality.selectCategory')">
<el-option :label="$t('nursing.quality.basicNursing')" value="BASIC" />
<el-option :label="$t('nursing.quality.specializedNursing')" value="SPECIALIZED" />
<el-option :label="$t('nursing.quality.nursingSafety')" value="SAFETY" />
<el-option :label="$t('nursing.quality.nursingDocumentation')" value="DOCUMENTATION" />
<el-option :label="$t('nursing.quality.sterilization')" value="STERILIZATION" />
</el-select>
</el-form-item>
<el-form-item label="目标值">
<el-input-number
v-model="formData.targetValue"
:min="0"
/>
<el-form-item :label="$t('nursing.quality.targetValue')">
<el-input-number v-model="formData.targetValue" :min="0" />
</el-form-item>
<el-form-item label="实际值">
<el-input-number
v-model="formData.actualValue"
:min="0"
/>
<el-form-item :label="$t('nursing.quality.actualValue')">
<el-input-number v-model="formData.actualValue" :min="0" />
</el-form-item>
<el-form-item label="科室">
<el-input
v-model="formData.departmentName"
placeholder="请输入科室"
/>
<el-form-item :label="$t('nursing.quality.department')">
<el-input v-model="formData.departmentName" :placeholder="$t('nursing.quality.departmentPlaceholder')" />
</el-form-item>
<el-form-item label="统计日期">
<el-date-picker
v-model="formData.statDate"
type="date"
placeholder="选择日期"
/>
<el-form-item :label="$t('nursing.quality.statDate')">
<el-date-picker v-model="formData.statDate" type="date" :placeholder="$t('nursing.quality.selectDate')" />
</el-form-item>
<el-form-item label="备注">
<el-input
v-model="formData.remark"
type="textarea"
:rows="2"
/>
<el-form-item :label="$t('nursing.quality.remark')">
<el-input v-model="formData.remark" type="textarea" :rows="2" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="showAdd = false">
取消
</el-button>
<el-button
type="primary"
@click="submitForm"
>
确定
</el-button>
<el-button @click="showAdd = false">{{ $t('nursing.quality.cancel') }}</el-button>
<el-button type="primary" @click="submitForm">{{ $t('nursing.quality.confirm') }}</el-button>
</template>
</el-dialog>
</div>
@@ -418,9 +157,11 @@
<script setup>
import {ref, reactive, onMounted, onUnmounted, nextTick, computed} from 'vue'
import {ElMessage} from 'element-plus'
import {useI18n} from 'vue-i18n'
import * as echarts from 'echarts'
import {getQualityPage, addIndicator, getQualitySummary, collectIndicators} from './api'
const {t} = useI18n()
const loading = ref(false)
const indicatorData = ref([])
const total = ref(0)
@@ -434,27 +175,39 @@ let trendChart = null
let deptChart = null
let categoryChart = null
const statCards = ref([
{label:'总指标', value:0, color:'#409eff'},
{label:'达标数', value:0, color:'#67c23a'},
{label:'未达标数', value:0, color:'#f56c6c'},
{label:'达标率', value:'0%', color:'#e6a23c'},
{label:'科室数', value:0, color:'#909399'},
{label:'平均达标率', value:'0%', color:'#409eff'}
const statCards = computed(() => [
{label: t('nursing.quality.totalIndicators'), value: 0, color: '#409eff'},
{label: t('nursing.quality.metCount'), value: 0, color: '#67c23a'},
{label: t('nursing.quality.unmetCount'), value: 0, color: '#f56c6c'},
{label: t('nursing.quality.meetRate'), value: '0%', color: '#e6a23c'},
{label: t('nursing.quality.deptCount'), value: 0, color: '#909399'},
{label: t('nursing.quality.avgMeetRate'), value: '0%', color: '#409eff'}
])
const q = ref({pageNo:1, pageSize: 10, indicatorCategory:'', departmentName:''})
const statCardsData = ref([
{value: 0}, {value: 0}, {value: 0}, {value: '0%'}, {value: 0}, {value: '0%'}
])
const q = ref({pageNo: 1, pageSize: 10, indicatorCategory: '', departmentName: ''})
const formData = reactive({
indicatorCode:'', indicatorName:'', indicatorCategory:'BASIC',
targetValue:0, actualValue:0, departmentName:'', statDate:'', remark:''
indicatorCode: '', indicatorName: '', indicatorCategory: 'BASIC',
targetValue: 0, actualValue: 0, departmentName: '', statDate: '', remark: ''
})
const unmetIndicators = computed(() => {
return indicatorData.value.filter(row => row.actualValue < row.targetValue)
})
const categoryMap = computed(() => ({
BASIC: t('nursing.quality.basicNursing'),
SPECIALIZED: t('nursing.quality.specializedNursing'),
SAFETY: t('nursing.quality.nursingSafety'),
DOCUMENTATION: t('nursing.quality.nursingDocumentation'),
STERILIZATION: t('nursing.quality.sterilization')
}))
function categoryText(c) {
return {BASIC:'基础护理',SPECIALIZED:'专科护理',SAFETY:'护理安全',DOCUMENTATION:'护理文书',STERILIZATION:'消毒隔离'}[c] || c
return categoryMap.value[c] || c
}
async function loadData() {
@@ -474,12 +227,12 @@ async function loadData() {
else unmet++
deptSet.add(row.departmentName)
})
statCards.value[0].value = total.value
statCards.value[1].value = met
statCards.value[2].value = unmet
statCards.value[3].value = total.value > 0 ? Math.round(met * 100 / total.value) + '%' : '0%'
statCards.value[4].value = deptSet.size
statCards.value[5].value = summary.value.meetRate ? summary.value.meetRate + '%' : '0%'
statCardsData.value[0].value = total.value
statCardsData.value[1].value = met
statCardsData.value[2].value = unmet
statCardsData.value[3].value = total.value > 0 ? Math.round(met * 100 / total.value) + '%' : '0%'
statCardsData.value[4].value = deptSet.size
statCardsData.value[5].value = summary.value.meetRate ? summary.value.meetRate + '%' : '0%'
nextTick(() => {
renderTrendChart()
@@ -491,20 +244,19 @@ async function loadData() {
function renderTrendChart() {
if (!trendChartRef.value) return
if (!trendChart) {
trendChart = echarts.init(trendChartRef.value)
}
if (!trendChart) { trendChart = echarts.init(trendChartRef.value) }
const months = ['1月', '2月', '3月', '4月', '5月', '6月']
const monthLabels = t('nursing.quality.monthLabels')
const months = Array.isArray(monthLabels) ? monthLabels : ['1月', '2月', '3月', '4月', '5月', '6月']
const meetRates = [85, 88, 82, 90, 87, 92]
trendChart.setOption({
tooltip: { trigger: 'axis' },
legend: { data: ['达标率'] },
legend: { data: [t('nursing.quality.meetRate')] },
xAxis: { type: 'category', data: months },
yAxis: { type: 'value', min: 0, max: 100, axisLabel: { formatter: '{value}%' } },
series: [{
name: '达标率',
name: t('nursing.quality.meetRate'),
type: 'line',
data: meetRates,
smooth: true,
@@ -516,19 +268,13 @@ function renderTrendChart() {
function renderDeptChart() {
if (!deptChartRef.value) return
if (!deptChart) {
deptChart = echarts.init(deptChartRef.value)
}
if (!deptChart) { deptChart = echarts.init(deptChartRef.value) }
const deptMap = {}
indicatorData.value.forEach(row => {
if (!deptMap[row.departmentName]) {
deptMap[row.departmentName] = { met: 0, total: 0 }
}
if (!deptMap[row.departmentName]) { deptMap[row.departmentName] = { met: 0, total: 0 } }
deptMap[row.departmentName].total++
if (row.actualValue >= row.targetValue) {
deptMap[row.departmentName].met++
}
if (row.actualValue >= row.targetValue) { deptMap[row.departmentName].met++ }
})
const depts = Object.keys(deptMap)
@@ -539,7 +285,7 @@ function renderDeptChart() {
xAxis: { type: 'category', data: depts },
yAxis: { type: 'value', min: 0, max: 100, axisLabel: { formatter: '{value}%' } },
series: [{
name: '达标率',
name: t('nursing.quality.meetRate'),
type: 'bar',
data: rates,
itemStyle: {
@@ -553,23 +299,21 @@ function renderDeptChart() {
function renderCategoryChart() {
if (!categoryChartRef.value) return
if (!categoryChart) {
categoryChart = echarts.init(categoryChartRef.value)
}
if (!categoryChart) { categoryChart = echarts.init(categoryChartRef.value) }
const categoryMap = {}
const catMap = {}
indicatorData.value.forEach(row => {
const cat = categoryText(row.indicatorCategory)
categoryMap[cat] = (categoryMap[cat] || 0) + 1
catMap[cat] = (catMap[cat] || 0) + 1
})
const data = Object.entries(categoryMap).map(([name, value]) => ({ name, value }))
const data = Object.entries(catMap).map(([name, value]) => ({ name, value }))
categoryChart.setOption({
tooltip: { trigger: 'item', formatter: '{b}: {c} ({d}%)' },
legend: { orient: 'vertical', left: 'left' },
series: [{
name: '指标类别',
name: t('nursing.quality.indicatorCategory'),
type: 'pie',
radius: '50%',
data: data,
@@ -581,26 +325,26 @@ function renderCategoryChart() {
}
function resetQuery() {
q.value = {pageNo:1, pageSize: 10, indicatorCategory:'', departmentName:''}
q.value = {pageNo: 1, pageSize: 10, indicatorCategory: '', departmentName: ''}
loadData()
}
async function submitForm() {
await addIndicator(formData)
ElMessage.success('新增成功')
ElMessage.success(t('nursing.quality.addSuccess'))
showAdd.value = false
loadData()
}
function exportReport() { ElMessage.info('导出功能开发中') }
function exportReport() { ElMessage.info(t('nursing.quality.exportDev')) }
async function handleCollect() {
try {
const res = await collectIndicators({ departmentName: q.value.departmentName || '' })
ElMessage.success(`已采集 ${res.data?.created || 0} 项指标`)
ElMessage.success(t('nursing.quality.collectSuccess', { count: res.data?.created || 0 }))
loadData()
} catch (e) {
ElMessage.error('采集失败')
ElMessage.error(t('nursing.quality.collectFailed'))
}
}