[update] Add new dependency for Times New Roman fonts in pom.xml. Update config.properties with new clinic details and toggle for auto status change. Enhance LocalStorage to track unread message count. Refactor CheckUpPage to remove PDF upload logic and improve video recording settings. Update NavBar to display unread message count and reset it upon opening chat. Modify AddDialog to include additional validation for patient information. Adjust MedicineDialog to allow editing of the route field. Implement custom close confirmation in ServerDashboard. Clean up ServerHandler by removing obsolete PDF upload logic. Update ultrasound report templates for better layout and presentation.
No files matched your search
@@ -1,13 +1,13 @@
|
||||
#BSK Clinic Management System Configuration - Updated via Settings Dialog
|
||||
#Sat Aug 02 11:33:13 ICT 2025
|
||||
clinic.address=So ABC, Duong XYZ, Quan 1, TP.HCM
|
||||
clinic.name=Phong kham BSK
|
||||
clinic.phone=0123456789
|
||||
clinic.prefix=BSK
|
||||
#Tue Aug 05 09:52:46 ICT 2025
|
||||
app.auto_change_status_to_finished=false
|
||||
clinic.address=112B/1 Khu ph\u1ED1 2, x\u00E3 \u0110\u1EE9c H\u00F2a, t\u1EC9nh T\u00E2y Ninh
|
||||
clinic.name=BS. NGUY\u1EC4N TH\u1ECA KH\u1EAEP
|
||||
clinic.phone=0908410007
|
||||
clinic.prefix=PH\u00D2NG KH\u00C1M S\u1EA2N PH\u1EE4 KHOA
|
||||
google.drive.enabled=true
|
||||
google.drive.root.folder=BSK_Clinic_Patient_Files
|
||||
server.address=127.0.0.1
|
||||
server.port=1999
|
||||
storage.checkup_media_base_dir=image/checkup_media
|
||||
storage.ultrasound_folder_path=ANH SIEU AM
|
||||
app.auto_change_status_to_finished=false
|
||||
|
After Width: | Height: | Size: 8.8 KiB |
|
After Width: | Height: | Size: 9.2 KiB |
|
After Width: | Height: | Size: 37 KiB |
|
After Width: | Height: | Size: 35 KiB |
@@ -246,5 +246,12 @@
|
||||
<artifactId>sqlite-jdbc</artifactId>
|
||||
<version>3.45.1.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.yourcompany</groupId>
|
||||
<artifactId>jasper-fonts-timesnewroman</artifactId>
|
||||
<version>1.0</version>
|
||||
<scope>system</scope>
|
||||
<systemPath>${project.basedir}/src/main/resources/fonts/timesnewroman-fonts.jar</systemPath>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -27,6 +27,7 @@ public class LocalStorage {
|
||||
public static Date dataDialogToDate = new Date();
|
||||
public static String dataDialogDoctorName = "Tất cả";
|
||||
public static String dataDialogIdSearchTerm = "";
|
||||
public static int unreadMessageCnt = 0;
|
||||
|
||||
public static List<DoctorItem> doctorsName = new ArrayList<>();
|
||||
public static String[] provinces = new String[]{};
|
||||
|
||||
@@ -1929,8 +1929,6 @@ public class CheckUpPage extends JPanel {
|
||||
JOptionPane.showMessageDialog(this, "Không thể hiển thị hộp thoại in: " + e.getMessage(), "Lỗi In", JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
|
||||
// Step 5: Upload PDF to backend after successful print dialog
|
||||
uploadPdfToBackend(ultrasoundResultPrint, "ultrasound_result", checkupIdField.getText());
|
||||
afterSaveActions(statusToSavePrint, "Đã lưu. Đang gửi lệnh in...");
|
||||
break;
|
||||
case "loupe": // This case now handles "Lưu & Xem"
|
||||
@@ -2004,8 +2002,6 @@ public class CheckUpPage extends JPanel {
|
||||
// Step 4: Finally, show the viewer with the pre-made object.
|
||||
JasperViewer.viewReport(jasperPrintToView, false);
|
||||
|
||||
// Step 5: Upload PDF to backend after successful view
|
||||
uploadPdfToBackend(ultrasoundResultView, "ultrasound_result", checkupIdField.getText());
|
||||
afterSaveActions(statusToSaveView, "Đã lưu. Đang tạo bản xem trước...");
|
||||
break;
|
||||
}
|
||||
@@ -3700,44 +3696,6 @@ public class CheckUpPage extends JPanel {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Uploads PDF to backend asynchronously
|
||||
* @param ultrasoundResult The UltrasoundResult object to export as PDF
|
||||
* @param pdfType The type of PDF ("ultrasound_result" or "medserinvoice")
|
||||
*/
|
||||
private void uploadPdfToBackend(UltrasoundResult ultrasoundResult, String pdfType, String checkupId) {
|
||||
log.info("uploadPdfToBackend");
|
||||
// Run PDF export and upload in background thread to avoid blocking UI
|
||||
CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
// Export PDF as bytes
|
||||
byte[] pdfBytes = ultrasoundResult.exportToPdfBytes();
|
||||
|
||||
// Create filename with override behavior (same name = override)
|
||||
String fileName = pdfType + ".pdf";
|
||||
|
||||
// Create and send upload request
|
||||
UploadCheckupPdfRequest request = new UploadCheckupPdfRequest(
|
||||
checkupId,
|
||||
pdfBytes,
|
||||
fileName,
|
||||
pdfType
|
||||
);
|
||||
|
||||
NetworkUtil.sendPacket(ClientHandler.ctx.channel(), request);
|
||||
log.info("Sent PDF upload request for checkup: {}, type: {}", checkupId, pdfType);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to export or upload PDF for checkup: {}", checkupId, e);
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
JOptionPane.showMessageDialog(this,
|
||||
"Lỗi khi tạo hoặc tải PDF: " + e.getMessage(),
|
||||
"Lỗi PDF",
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void handleRecordVideo() {
|
||||
if (currentCheckupMediaPath == null) {
|
||||
@@ -3765,28 +3723,25 @@ public class CheckUpPage extends JPanel {
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new Exception("Không tìm thấy thư viện JavaCV cần thiết. Vui lòng kiểm tra cài đặt Maven.", e);
|
||||
}
|
||||
|
||||
|
||||
// Initialize frame converter
|
||||
frameConverter = new Java2DFrameConverter();
|
||||
|
||||
|
||||
// Get webcam's actual frame rate
|
||||
double webcamFPS = selectedWebcam.getFPS();
|
||||
// If webcam doesn't report FPS or reports an invalid value, default to 15
|
||||
// If webcam doesn't report FPS or reports an invalid value, default to a base value like 15
|
||||
if (webcamFPS <= 0 || Double.isNaN(webcamFPS)) {
|
||||
webcamFPS = 15.0;
|
||||
}
|
||||
|
||||
// captureRate is used by the capture loop logic for polling interval
|
||||
final double captureRate = webcamFPS;
|
||||
|
||||
// If observed playback is 2x faster, the actual sustainable FPS for the recorder
|
||||
// should be half the reported/assumed FPS.
|
||||
double recorderFrameRate = webcamFPS / 2.0;
|
||||
if (recorderFrameRate < 1.0) { // Ensure a minimum frame rate of 1.0
|
||||
recorderFrameRate = 1.0;
|
||||
}
|
||||
log.info("Setting recorder frame rate to: {} (derived from webcam FPS: {})", String.format("%.2f", recorderFrameRate), String.format("%.2f", webcamFPS));
|
||||
|
||||
// FIX FOR TIMING ISSUE: We double the reported FPS to match the actual frame delivery speed.
|
||||
final double recorderFrameRate = webcamFPS * 2.0;
|
||||
|
||||
// The capture loop will now also target this corrected frame rate to avoid missing frames.
|
||||
final double captureRate = recorderFrameRate;
|
||||
|
||||
log.info("Setting recorder frame rate to: {} (Corrected from webcam's reported FPS of {})", String.format("%.2f", recorderFrameRate), String.format("%.2f", webcamFPS));
|
||||
|
||||
// Initialize the recorder with optimized settings
|
||||
try {
|
||||
Dimension size = selectedWebcam.getViewSize();
|
||||
@@ -3795,23 +3750,24 @@ public class CheckUpPage extends JPanel {
|
||||
// Video format and codec settings
|
||||
recorder.setVideoCodec(org.bytedeco.ffmpeg.global.avcodec.AV_CODEC_ID_H264);
|
||||
recorder.setFormat("mp4");
|
||||
// Use the adjusted effective frame rate for the recorder
|
||||
recorder.setFrameRate(recorderFrameRate);
|
||||
recorder.setVideoQuality(1);
|
||||
|
||||
// Pixel format and color space settings
|
||||
// Set the corrected frame rate
|
||||
recorder.setFrameRate(recorderFrameRate);
|
||||
|
||||
// Pixel format
|
||||
recorder.setPixelFormat(org.bytedeco.ffmpeg.global.avutil.AV_PIX_FMT_YUV420P);
|
||||
|
||||
// Encoding settings for better performance
|
||||
recorder.setVideoOption("preset", "medium");
|
||||
// --- QUALITY SETTINGS ---
|
||||
// Using CRF is the best way to control quality for H.264.
|
||||
// The setVideoQuality(0) call is removed to avoid conflicts.
|
||||
recorder.setVideoOption("crf", "18"); // High quality
|
||||
recorder.setVideoOption("preset", "slow"); // Better compression
|
||||
recorder.setVideoOption("tune", "zerolatency");
|
||||
recorder.setVideoOption("crf", "23");
|
||||
|
||||
// Buffer size settings - increased for smoother recording
|
||||
recorder.setVideoOption("bufsize", "5000k");
|
||||
recorder.setVideoOption("maxrate", "5000k");
|
||||
// Buffer size settings
|
||||
recorder.setVideoOption("bufsize", "20000k");
|
||||
recorder.setVideoOption("maxrate", "20000k");
|
||||
|
||||
// Remove the speed filter and start recorder
|
||||
recorder.start();
|
||||
} catch (Exception e) {
|
||||
throw new Exception("Lỗi khởi tạo FFmpeg recorder: " + e.getMessage(), e);
|
||||
|
||||
@@ -721,7 +721,7 @@ public class AddDialog extends JDialog {
|
||||
|
||||
// Validate required fields
|
||||
if (patientName.isEmpty() || patientPhone.isEmpty() || customerAddress.isEmpty() ||
|
||||
dobPicker.getJFormattedTextField().getText().isEmpty()) {
|
||||
dobPicker.getJFormattedTextField().getText().isEmpty() || cccd.isEmpty()) {
|
||||
JOptionPane.showMessageDialog(this, "Vui lòng điền đầy đủ thông tin");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -271,7 +271,7 @@ public class MedicineDialog extends JDialog {
|
||||
routeField = new JTextField(20);
|
||||
routeField.setFont(textFont);
|
||||
routeField.setPreferredSize(textFieldSize);
|
||||
routeField.setEditable(false);
|
||||
routeField.setEditable(true);
|
||||
medicineInfoPanel.add(routeField, gbc);
|
||||
gbc.weightx = 0.0;
|
||||
// --- END ADDED ---
|
||||
|
||||
@@ -31,6 +31,8 @@ public class NavBar extends JPanel {
|
||||
|
||||
private JLabel activeNavItem = null;
|
||||
private final MainFrame mainFrame;
|
||||
private JLabel unreadCountLabel; // NEW: Label to display unread count
|
||||
private RoundedPanel messageButtonPanel; // NEW: Reference to message button panel for repainting
|
||||
|
||||
// Listener for incoming emergency alerts
|
||||
private final ResponseListener<EmergencyResponse> emergencyResponseListener = this::handleEmergencyResponse;
|
||||
@@ -275,6 +277,10 @@ public class NavBar extends JPanel {
|
||||
|
||||
private void showNotificationForMessage(SimpleMessageResponse response) {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
// Increment unread message count
|
||||
LocalStorage.unreadMessageCnt++;
|
||||
updateUnreadCountDisplay();
|
||||
|
||||
// Create a borderless window for the notification
|
||||
JWindow notificationWindow = new JWindow(mainFrame);
|
||||
|
||||
@@ -336,6 +342,26 @@ public class NavBar extends JPanel {
|
||||
log.info("NavBar message listener re-enabled.");
|
||||
}
|
||||
|
||||
// NEW: Method to reset unread count when chat dialog opens
|
||||
public void resetUnreadMessageCount() {
|
||||
LocalStorage.unreadMessageCnt = 0;
|
||||
updateUnreadCountDisplay();
|
||||
log.info("Unread message count reset to 0.");
|
||||
}
|
||||
|
||||
// NEW: Method to update the unread count display
|
||||
private void updateUnreadCountDisplay() {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
if (LocalStorage.unreadMessageCnt > 0) {
|
||||
unreadCountLabel.setText(String.valueOf(LocalStorage.unreadMessageCnt));
|
||||
unreadCountLabel.setVisible(true);
|
||||
} else {
|
||||
unreadCountLabel.setVisible(false);
|
||||
}
|
||||
messageButtonPanel.repaint();
|
||||
});
|
||||
}
|
||||
|
||||
private Component createEmergencyButton() {
|
||||
Color emergencyColor = new Color(220, 38, 38);
|
||||
RoundedPanel buttonPanel = new RoundedPanel(15, emergencyColor, true);
|
||||
@@ -435,22 +461,23 @@ public class NavBar extends JPanel {
|
||||
|
||||
private Component createMessageButton() {
|
||||
Color messageColor = new Color(150, 150, 150); // Muted gray color
|
||||
RoundedPanel buttonPanel = new RoundedPanel(15, messageColor, true);
|
||||
buttonPanel.setLayout(new BorderLayout(2, 2));
|
||||
buttonPanel.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
|
||||
buttonPanel.setToolTipText("Tin nhắn"); // Add a tooltip
|
||||
messageButtonPanel = new RoundedPanel(15, messageColor, true); // Store reference
|
||||
messageButtonPanel.setLayout(null); // Use null layout for absolute positioning
|
||||
messageButtonPanel.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
|
||||
messageButtonPanel.setToolTipText("Tin nhắn"); // Add a tooltip
|
||||
|
||||
// Make the button smaller and square-like
|
||||
buttonPanel.setPreferredSize(new Dimension(55, 55));
|
||||
buttonPanel.setBorder(BorderFactory.createCompoundBorder(
|
||||
messageButtonPanel.setPreferredSize(new Dimension(55, 55));
|
||||
messageButtonPanel.setBorder(BorderFactory.createCompoundBorder(
|
||||
BorderFactory.createEmptyBorder(2, 2, 2, 2),
|
||||
BorderFactory.createEmptyBorder(5, 10, 5, 10)
|
||||
));
|
||||
|
||||
// Create the label without text
|
||||
// Create the main icon label
|
||||
JLabel buttonLabel = new JLabel();
|
||||
buttonLabel.setHorizontalAlignment(SwingConstants.CENTER);
|
||||
buttonLabel.setOpaque(false);
|
||||
buttonLabel.setBounds(0, 0, 55, 55); // Position the main icon
|
||||
|
||||
try {
|
||||
String iconPath = "src/main/java/BsK/client/ui/assets/icon/chat.png";
|
||||
@@ -463,11 +490,33 @@ public class NavBar extends JPanel {
|
||||
buttonLabel.setText("Msg"); // Fallback text if icon fails
|
||||
}
|
||||
|
||||
buttonPanel.add(buttonLabel, BorderLayout.CENTER);
|
||||
// Create the unread count badge
|
||||
unreadCountLabel = new JLabel();
|
||||
unreadCountLabel.setOpaque(true);
|
||||
unreadCountLabel.setBackground(Color.RED);
|
||||
unreadCountLabel.setForeground(Color.WHITE);
|
||||
unreadCountLabel.setFont(new Font("Arial", Font.BOLD, 10));
|
||||
unreadCountLabel.setHorizontalAlignment(SwingConstants.CENTER);
|
||||
unreadCountLabel.setBounds(35, 5, 18, 18); // Position in top-right corner
|
||||
unreadCountLabel.setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1));
|
||||
|
||||
// Make the badge circular
|
||||
unreadCountLabel.setPreferredSize(new Dimension(18, 18));
|
||||
unreadCountLabel.setMinimumSize(new Dimension(18, 18));
|
||||
unreadCountLabel.setMaximumSize(new Dimension(18, 18));
|
||||
|
||||
// Initially hide the badge
|
||||
unreadCountLabel.setVisible(false);
|
||||
|
||||
buttonPanel.addMouseListener(new MouseAdapter() {
|
||||
messageButtonPanel.add(buttonLabel);
|
||||
messageButtonPanel.add(unreadCountLabel);
|
||||
|
||||
messageButtonPanel.addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
// Reset unread count when opening chat
|
||||
resetUnreadMessageCount();
|
||||
|
||||
// Open the SimpleChatDialog
|
||||
SimpleChatDialog chatDialog = new SimpleChatDialog(mainFrame, NavBar.this);
|
||||
chatDialog.setVisible(true);
|
||||
@@ -475,16 +524,16 @@ public class NavBar extends JPanel {
|
||||
|
||||
@Override
|
||||
public void mouseEntered(MouseEvent e) {
|
||||
buttonPanel.setBackground(messageColor.darker());
|
||||
messageButtonPanel.setBackground(messageColor.darker());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseExited(MouseEvent e) {
|
||||
buttonPanel.setBackground(messageColor);
|
||||
messageButtonPanel.setBackground(messageColor);
|
||||
}
|
||||
});
|
||||
|
||||
return buttonPanel;
|
||||
return messageButtonPanel;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -5,6 +5,8 @@ import javax.swing.border.TitledBorder;
|
||||
import javax.swing.table.DefaultTableModel;
|
||||
import javax.swing.text.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.WindowAdapter;
|
||||
import java.awt.event.WindowEvent;
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.lang.management.MemoryMXBean;
|
||||
import java.lang.management.OperatingSystemMXBean;
|
||||
@@ -35,8 +37,6 @@ public class ServerDashboard extends JFrame {
|
||||
private boolean isAutoScrollEnabled = true;
|
||||
private JTable networkTable;
|
||||
private DefaultTableModel networkTableModel;
|
||||
|
||||
// --- NEW ---
|
||||
private JButton backupDbButton;
|
||||
|
||||
public static ServerDashboard getInstance() {
|
||||
@@ -49,8 +49,11 @@ public class ServerDashboard extends JFrame {
|
||||
private ServerDashboard() {
|
||||
setTitle("BSK Server Dashboard");
|
||||
setSize(1200, 800);
|
||||
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
setLocationRelativeTo(null);
|
||||
|
||||
// --- MODIFICATION START: Add custom close logic ---
|
||||
addCustomCloseListener();
|
||||
// --- MODIFICATION END ---
|
||||
|
||||
// Main container panel
|
||||
JPanel mainPanel = new JPanel(new BorderLayout(10, 10));
|
||||
@@ -66,7 +69,12 @@ public class ServerDashboard extends JFrame {
|
||||
startSystemStatsTimer();
|
||||
startNetworkStatsTimer();
|
||||
}
|
||||
|
||||
|
||||
// Unchanged methods from your original code are here...
|
||||
// createHeaderPanel(), performDatabaseBackup(), createMainContentPanel(), etc.
|
||||
// I've omitted them for brevity but they should remain in your file.
|
||||
|
||||
// --- START OF UNCHANGED METHODS (for context) ---
|
||||
private JPanel createHeaderPanel() {
|
||||
JPanel headerPanel = new JPanel(new GridBagLayout());
|
||||
GridBagConstraints gbc = new GridBagConstraints();
|
||||
@@ -127,7 +135,6 @@ public class ServerDashboard extends JFrame {
|
||||
return headerPanel;
|
||||
}
|
||||
|
||||
// --- NEW: Method to handle the backup action ---
|
||||
private void performDatabaseBackup() {
|
||||
// Disable button to prevent multiple clicks
|
||||
backupDbButton.setEnabled(false);
|
||||
@@ -153,10 +160,10 @@ public class ServerDashboard extends JFrame {
|
||||
// Update UI on failure
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
addLog("❌ Database backup failed: " + e.getMessage());
|
||||
JOptionPane.showMessageDialog(this,
|
||||
"Failed to backup the database.\nError: " + e.getMessage() + "\n\nCheck logs for more details.",
|
||||
"Backup Failed",
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
JOptionPane.showMessageDialog(this,
|
||||
"Failed to backup the database.\nError: " + e.getMessage() + "\n\nCheck logs for more details.",
|
||||
"Backup Failed",
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
});
|
||||
} finally {
|
||||
// Always re-enable the button on the UI thread
|
||||
@@ -166,7 +173,6 @@ public class ServerDashboard extends JFrame {
|
||||
}
|
||||
|
||||
private JSplitPane createMainContentPanel() {
|
||||
// ... (This method remains unchanged)
|
||||
// --- Log Panel ---
|
||||
JPanel logPanel = new JPanel(new BorderLayout());
|
||||
logPanel.setBorder(BorderFactory.createTitledBorder("Server Logs"));
|
||||
@@ -197,7 +203,6 @@ public class ServerDashboard extends JFrame {
|
||||
}
|
||||
|
||||
private JPanel createLogControlsPanel() {
|
||||
// ... (This method remains unchanged)
|
||||
JPanel logControlsPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
|
||||
searchField = new JTextField(20);
|
||||
JButton searchButton = new JButton("Search");
|
||||
@@ -218,7 +223,6 @@ public class ServerDashboard extends JFrame {
|
||||
}
|
||||
|
||||
private void startSystemStatsTimer() {
|
||||
// ... (This method remains unchanged)
|
||||
MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
|
||||
OperatingSystemMXBean osBean = ManagementFactory.getOperatingSystemMXBean();
|
||||
|
||||
@@ -240,13 +244,11 @@ public class ServerDashboard extends JFrame {
|
||||
}
|
||||
|
||||
private void startNetworkStatsTimer() {
|
||||
// ... (This method remains unchanged)
|
||||
networkStatsTimer = new Timer(1000, e -> refreshNetworkTable());
|
||||
networkStatsTimer.start();
|
||||
}
|
||||
|
||||
public void refreshNetworkTable() {
|
||||
// ... (This method remains unchanged)
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
networkTableModel.setRowCount(0);
|
||||
for (ClientConnection conn : SessionManager.getAllConnections()) {
|
||||
@@ -263,7 +265,6 @@ public class ServerDashboard extends JFrame {
|
||||
}
|
||||
|
||||
private void searchInLogs() {
|
||||
// ... (This method remains unchanged)
|
||||
String searchText = searchField.getText().toLowerCase();
|
||||
if (searchText.isEmpty()) {
|
||||
return;
|
||||
@@ -288,7 +289,6 @@ public class ServerDashboard extends JFrame {
|
||||
}
|
||||
|
||||
private void clearLogs() {
|
||||
// ... (This method remains unchanged)
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
try {
|
||||
logArea.getDocument().remove(0, logArea.getDocument().getLength());
|
||||
@@ -300,7 +300,6 @@ public class ServerDashboard extends JFrame {
|
||||
}
|
||||
|
||||
public void updateStatus(String status, Color color) {
|
||||
// ... (This method remains unchanged)
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
statusLabel.setText("Status: " + status);
|
||||
statusLabel.setForeground(color);
|
||||
@@ -308,26 +307,22 @@ public class ServerDashboard extends JFrame {
|
||||
}
|
||||
|
||||
public void updatePort(int port) {
|
||||
// ... (This method remains unchanged)
|
||||
SwingUtilities.invokeLater(() ->
|
||||
portLabel.setText("Port: " + port)
|
||||
);
|
||||
}
|
||||
|
||||
public static void incrementClients() {
|
||||
// ... (This method remains unchanged)
|
||||
connectedClients++;
|
||||
updateClientCount();
|
||||
}
|
||||
|
||||
public static void decrementClients() {
|
||||
// ... (This method remains unchanged)
|
||||
connectedClients = Math.max(0, connectedClients - 1);
|
||||
updateClientCount();
|
||||
}
|
||||
|
||||
private static void updateClientCount() {
|
||||
// ... (This method remains unchanged)
|
||||
if (instance != null) {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
instance.clientsLabel.setText("Connected Clients: " + connectedClients);
|
||||
@@ -337,7 +332,6 @@ public class ServerDashboard extends JFrame {
|
||||
}
|
||||
|
||||
public void addLog(String message) {
|
||||
// ... (This method remains unchanged)
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
try {
|
||||
Document doc = logArea.getDocument();
|
||||
@@ -360,7 +354,6 @@ public class ServerDashboard extends JFrame {
|
||||
}
|
||||
|
||||
public void updateGoogleDriveStatus(boolean connected, String statusMessage) {
|
||||
// ... (This method remains unchanged)
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
if (connected) {
|
||||
googleDriveLabel.setText("✅ " + statusMessage);
|
||||
@@ -377,7 +370,6 @@ public class ServerDashboard extends JFrame {
|
||||
}
|
||||
|
||||
private void retryGoogleDriveConnection() {
|
||||
// ... (This method remains unchanged)
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
googleDriveLabel.setText("🔄 Connecting...");
|
||||
googleDriveLabel.setForeground(Color.ORANGE);
|
||||
@@ -413,7 +405,6 @@ public class ServerDashboard extends JFrame {
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
// ... (This method remains unchanged)
|
||||
if (statsTimer != null) {
|
||||
statsTimer.stop();
|
||||
}
|
||||
@@ -422,4 +413,45 @@ public class ServerDashboard extends JFrame {
|
||||
}
|
||||
super.dispose();
|
||||
}
|
||||
// --- END OF UNCHANGED METHODS ---
|
||||
|
||||
// --- MODIFICATION START: Add new methods for close confirmation ---
|
||||
/**
|
||||
* Overrides the default close operation to show a confirmation dialog.
|
||||
*/
|
||||
private void addCustomCloseListener() {
|
||||
// This tells the frame to do nothing automatically when the 'X' is clicked.
|
||||
// We will handle the closing manually.
|
||||
setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
|
||||
|
||||
addWindowListener(new WindowAdapter() {
|
||||
@Override
|
||||
public void windowClosing(WindowEvent e) {
|
||||
showExitConfirmationDialog();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the confirmation dialog and exits the application if confirmed.
|
||||
*/
|
||||
private void showExitConfirmationDialog() {
|
||||
String title = "Xác nhận thoát";
|
||||
String message = "Nếu tắt bạn sẽ đóng tất cả các máy con. Bạn có chắc chắn không?";
|
||||
|
||||
int result = JOptionPane.showConfirmDialog(
|
||||
this,
|
||||
message,
|
||||
title,
|
||||
JOptionPane.YES_NO_OPTION,
|
||||
JOptionPane.WARNING_MESSAGE);
|
||||
|
||||
if (result == JOptionPane.YES_OPTION) {
|
||||
// User confirmed, so exit the entire application.
|
||||
// This will trigger the graceful shutdown logic in your Server's main() method.
|
||||
System.exit(0);
|
||||
}
|
||||
// If the user chooses "No", the dialog closes and nothing else happens.
|
||||
}
|
||||
// --- MODIFICATION END ---
|
||||
}
|
||||
@@ -1168,43 +1168,6 @@ public class ServerHandler extends SimpleChannelInboundHandler<TextWebSocketFram
|
||||
}
|
||||
}
|
||||
|
||||
if (packet instanceof UploadCheckupPdfRequest uploadCheckupPdfRequest) {
|
||||
log.info("Received UploadCheckupPdfRequest for checkupId: {}, type: {}", uploadCheckupPdfRequest.getCheckupId(), uploadCheckupPdfRequest.getPdfType());
|
||||
|
||||
String checkupId = uploadCheckupPdfRequest.getCheckupId();
|
||||
String fileName = uploadCheckupPdfRequest.getFileName();
|
||||
String pdfType = uploadCheckupPdfRequest.getPdfType();
|
||||
byte[] pdfData = uploadCheckupPdfRequest.getPdfData();
|
||||
|
||||
if (checkupId == null || checkupId.trim().isEmpty()) {
|
||||
UserUtil.sendPacket(currentUser.getSessionId(), new UploadCheckupPdfResponse(false, "CheckupID is null or empty. Cannot save PDF.", fileName, pdfType));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 1. Define and create storage directory (use same img_db structure)
|
||||
Path storageDir = Paths.get(Server.imageDbPath, checkupId.trim());
|
||||
Files.createDirectories(storageDir); // Create dirs if they don't exist
|
||||
|
||||
// 2. Save the PDF file (with override behavior - same name = override)
|
||||
Path filePath = storageDir.resolve(fileName);
|
||||
try (FileOutputStream fos = new FileOutputStream(filePath.toFile())) {
|
||||
fos.write(pdfData);
|
||||
}
|
||||
String savedPath = filePath.toString().replace("\\", "/");
|
||||
log.info("Successfully saved PDF to {} (override mode)", savedPath);
|
||||
|
||||
// 3. Send immediate success response to client
|
||||
UserUtil.sendPacket(currentUser.getSessionId(), new UploadCheckupPdfResponse(true, "PDF uploaded successfully to " + savedPath, fileName, pdfType));
|
||||
|
||||
// 4. Upload to Google Drive asynchronously (don't block the response)
|
||||
uploadCheckupPdfToGoogleDriveAsync(checkupId, fileName, pdfData, pdfType);
|
||||
|
||||
} catch (IOException e) {
|
||||
log.error("Failed to save uploaded PDF for checkupId {}: {}", checkupId, e.getMessage());
|
||||
UserUtil.sendPacket(currentUser.getSessionId(), new UploadCheckupPdfResponse(false, "Server failed to save PDF: " + e.getMessage(), fileName, pdfType));
|
||||
}
|
||||
}
|
||||
|
||||
if (packet instanceof GetImagesByCheckupIdReq getImagesByCheckupIdReq) {
|
||||
String checkupId = getImagesByCheckupIdReq.getCheckupId();
|
||||
@@ -2294,84 +2257,7 @@ public class ServerHandler extends SimpleChannelInboundHandler<TextWebSocketFram
|
||||
return tempFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tải tệp PDF của lần khám lên Google Drive một cách bất đồng bộ.
|
||||
*
|
||||
* @param checkupId ID của lần khám (dưới dạng String).
|
||||
* @param fileName Tên của tệp PDF.
|
||||
* @param pdfData Dữ liệu của tệp PDF.
|
||||
* @param pdfType Loại PDF (ví dụ: "ultrasound_result").
|
||||
*/
|
||||
private void uploadCheckupPdfToGoogleDriveAsync(String checkupId, String fileName, byte[] pdfData, String pdfType) {
|
||||
// CẢI TIẾN: Sử dụng CompletableFuture để xử lý bất đồng bộ, hiệu quả hơn new Thread().
|
||||
CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
if (!Server.isGoogleDriveConnected()) {
|
||||
log.info("Google Drive not connected - skipping PDF upload for checkup {}", checkupId);
|
||||
return;
|
||||
}
|
||||
|
||||
// --- SỬA LỖI: Lấy thông tin drive_folder_id từ CSDL một cách an toàn ---
|
||||
String checkupDriveFolderId = null;
|
||||
String sql = "SELECT drive_folder_id FROM Checkup WHERE checkup_id = ?";
|
||||
|
||||
try (Connection conn = DatabaseManager.getConnection();
|
||||
PreparedStatement stmt = conn.prepareStatement(sql)) {
|
||||
|
||||
// Giả định checkup_id trong CSDL là kiểu số (integer)
|
||||
stmt.setInt(1, Integer.parseInt(checkupId));
|
||||
|
||||
try (ResultSet rs = stmt.executeQuery()) {
|
||||
if (rs.next()) {
|
||||
checkupDriveFolderId = rs.getString("drive_folder_id");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (checkupDriveFolderId == null || checkupDriveFolderId.trim().isEmpty()) {
|
||||
log.warn("Checkup {} has no Google Drive folder ID - cannot upload PDF", checkupId);
|
||||
return;
|
||||
}
|
||||
|
||||
log.info("Uploading PDF {} ({}) to Google Drive for checkup {}", fileName, pdfType, checkupId);
|
||||
|
||||
// Logic tạo và dọn dẹp file tạm của bạn đã tốt, chúng ta giữ lại
|
||||
java.io.File tempFile = null;
|
||||
try {
|
||||
tempFile = createTempFileFromBytes(pdfData, fileName);
|
||||
|
||||
String uploadedFileId = Server.getGoogleDriveService().uploadFileToFolder(
|
||||
checkupDriveFolderId, tempFile, fileName
|
||||
);
|
||||
|
||||
log.info("PDF uploaded successfully to Google Drive for checkup {}: FileID={}", checkupId, uploadedFileId);
|
||||
|
||||
if (ServerDashboard.getInstance() != null) {
|
||||
ServerDashboard.getInstance().addLog(
|
||||
String.format("Uploaded PDF %s (%s) to Google Drive for checkup %s", fileName, pdfType, checkupId)
|
||||
);
|
||||
}
|
||||
} finally {
|
||||
if (tempFile != null && tempFile.exists()) {
|
||||
if (!tempFile.delete()) {
|
||||
log.warn("Could not delete temporary PDF file: {}", tempFile.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} catch (NumberFormatException e) {
|
||||
log.error("Invalid checkupId format '{}' for PDF upload.", checkupId);
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to upload PDF to Google Drive for checkup {}: {}", checkupId, e.getMessage(), e);
|
||||
if (ServerDashboard.getInstance() != null) {
|
||||
ServerDashboard.getInstance().addLog(
|
||||
String.format("Failed to upload PDF %s (%s) for checkup %s: %s", fileName, pdfType, checkupId, e.getMessage())
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private void broadcastQueueUpdate() {
|
||||
// SỬA ĐỔI: Sử dụng một kết nối duy nhất, an toàn cho tất cả các truy vấn bên trong
|
||||
try (Connection conn = DatabaseManager.getConnection()) {
|
||||
|
||||
@@ -51,55 +51,49 @@
|
||||
<element kind="textField" uuid="eb56d4c8-2a0d-45fa-89a2-ad6528f9a99b" x="2" y="0" width="287" height="20" forecolor="#FF0905" fontName="Times New Roman" fontSize="14.0" bold="true" hTextAlign="Center">
|
||||
<expression><![CDATA[$P{clinicPrefix}]]></expression>
|
||||
</element>
|
||||
<element kind="textField" uuid="796d9ffd-6d2e-4ae3-bdfb-b08183d7d9c0" x="2" y="30" width="287" height="20" fontName="Times New Roman" fontSize="10.0" bold="true">
|
||||
<expression><![CDATA["Địa chỉ: "+ $P{clinicAddress}]]></expression>
|
||||
</element>
|
||||
<element kind="textField" uuid="260bb33d-1d11-4178-aceb-3fbf8fcb4b45" x="2" y="44" width="287" height="12" fontName="Times New Roman" fontSize="8.0" bold="true">
|
||||
<expression><![CDATA["SĐT: " + $P{clinicPhone}]]></expression>
|
||||
</element>
|
||||
<element kind="staticText" uuid="6f89a93f-2d10-4506-baa5-226bdd047b17" x="0" y="65" width="100" height="30" forecolor="#FF0905" fontName="Times New Roman" fontSize="14.0" bold="true">
|
||||
<element kind="staticText" uuid="6f89a93f-2d10-4506-baa5-226bdd047b17" x="0" y="51" width="100" height="30" forecolor="#FF0905" fontName="Times New Roman" fontSize="14.0" bold="true">
|
||||
<text><![CDATA[I.Thông tin]]></text>
|
||||
</element>
|
||||
<element kind="staticText" uuid="b8dfca43-cf97-49e5-b58d-4667143dba62" x="0" y="79" width="50" height="15" fontName="Times New Roman" fontSize="10.0" bold="true" hTextAlign="Left">
|
||||
<element kind="staticText" uuid="b8dfca43-cf97-49e5-b58d-4667143dba62" x="0" y="69" width="60" height="15" fontName="Times New Roman" fontSize="12.0" bold="true" hTextAlign="Left">
|
||||
<text><![CDATA[Họ và tên:]]></text>
|
||||
</element>
|
||||
<element kind="staticText" uuid="0a9ca0c8-f2bd-4ade-a025-a6d79c13613f" x="1" y="94" width="80" height="15" fontName="Times New Roman" fontSize="10.0" bold="true" hTextAlign="Left">
|
||||
<element kind="staticText" uuid="0a9ca0c8-f2bd-4ade-a025-a6d79c13613f" x="1" y="84" width="80" height="15" fontName="Times New Roman" fontSize="12.0" bold="true" hTextAlign="Left">
|
||||
<text><![CDATA[Ngày sinh:]]></text>
|
||||
</element>
|
||||
<element kind="staticText" uuid="21917ab8-ae07-44c8-ba4e-bc6c4c54b440" x="140" y="94" width="80" height="15" fontName="Times New Roman" fontSize="10.0" bold="true" hTextAlign="Left">
|
||||
<element kind="staticText" uuid="21917ab8-ae07-44c8-ba4e-bc6c4c54b440" x="131" y="84" width="80" height="15" fontName="Times New Roman" fontSize="12.0" bold="true" hTextAlign="Left">
|
||||
<text><![CDATA[Giới tính:]]></text>
|
||||
</element>
|
||||
<element kind="staticText" uuid="bcbb614e-7a4d-48a3-83c2-198e4fb62c5f" x="1" y="109" width="49" height="15" fontName="Times New Roman" bold="true" hTextAlign="Left">
|
||||
<element kind="staticText" uuid="bcbb614e-7a4d-48a3-83c2-198e4fb62c5f" x="1" y="100" width="49" height="15" fontName="Times New Roman" fontSize="12.0" bold="true" hTextAlign="Left">
|
||||
<text><![CDATA[Địa chỉ:]]></text>
|
||||
</element>
|
||||
<element kind="staticText" uuid="45fe4be1-9e21-459b-aeee-2efc8354f372" x="221" y="80" width="68" height="15" fontName="Times New Roman" fontSize="10.0" bold="true" hTextAlign="Left">
|
||||
<element kind="staticText" uuid="45fe4be1-9e21-459b-aeee-2efc8354f372" x="201" y="69" width="80" height="15" fontName="Times New Roman" fontSize="12.0" bold="true" hTextAlign="Left">
|
||||
<text><![CDATA[Bác sĩ chỉ định:]]></text>
|
||||
</element>
|
||||
<element kind="staticText" uuid="f2ff4b17-63cc-4e8c-a3b0-2523f534f21a" x="221" y="94" width="60" height="15" fontName="Times New Roman" fontSize="10.0" bold="true" hTextAlign="Left">
|
||||
<element kind="staticText" uuid="f2ff4b17-63cc-4e8c-a3b0-2523f534f21a" x="217" y="84" width="70" height="15" fontName="Times New Roman" fontSize="12.0" bold="true" hTextAlign="Left">
|
||||
<text><![CDATA[Ngày khám:]]></text>
|
||||
</element>
|
||||
<element kind="textField" uuid="1511f53a-b282-41b1-b233-612b6106c07a" x="48" y="79" width="172" height="15" fontName="Times New Roman" fontSize="10.0">
|
||||
<element kind="textField" uuid="1511f53a-b282-41b1-b233-612b6106c07a" x="57" y="69" width="143" height="15" fontName="Times New Roman" fontSize="12.0">
|
||||
<expression><![CDATA[$P{patientName}]]></expression>
|
||||
</element>
|
||||
<element kind="textField" uuid="5bdac267-523d-4931-926b-76233ce9b84d" x="50" y="94" width="80" height="15" fontName="Times New Roman" fontSize="10.0">
|
||||
<element kind="textField" uuid="5bdac267-523d-4931-926b-76233ce9b84d" x="57" y="84" width="74" height="15" fontName="Times New Roman" fontSize="12.0">
|
||||
<expression><![CDATA[$P{patientDOB}]]></expression>
|
||||
</element>
|
||||
<element kind="textField" uuid="f4df20b4-920c-491e-882b-6e0733cb4866" x="184" y="94" width="36" height="15" fontName="Times New Roman" fontSize="10.0">
|
||||
<element kind="textField" uuid="f4df20b4-920c-491e-882b-6e0733cb4866" x="182" y="84" width="36" height="15" fontName="Times New Roman" fontSize="12.0">
|
||||
<expression><![CDATA[$P{patientGender}]]></expression>
|
||||
</element>
|
||||
<element kind="textField" uuid="3b8ac40e-85aa-4ab8-8445-64bcddb8b267" x="39" y="109" width="359" height="15" fontName="Times New Roman" fontSize="10.0">
|
||||
<element kind="textField" uuid="3b8ac40e-85aa-4ab8-8445-64bcddb8b267" x="57" y="100" width="341" height="15" fontName="Times New Roman" fontSize="12.0">
|
||||
<expression><![CDATA[$P{patientAddress}]]></expression>
|
||||
</element>
|
||||
<element kind="textField" uuid="db40cdb0-2850-4d66-8430-addc54f90321" x="290" y="80" width="108" height="15" fontName="Times New Roman" fontSize="10.0">
|
||||
<element kind="textField" uuid="db40cdb0-2850-4d66-8430-addc54f90321" x="281" y="69" width="116" height="15" fontName="Times New Roman" fontSize="12.0">
|
||||
<expression><![CDATA[$P{doctorName}]]></expression>
|
||||
</element>
|
||||
<element kind="textField" uuid="eb9cf54a-3b50-43c5-8696-cfc1e21bddfe" x="276" y="94" width="122" height="15" fontName="Times New Roman" fontSize="10.0">
|
||||
<element kind="textField" uuid="eb9cf54a-3b50-43c5-8696-cfc1e21bddfe" x="281" y="84" width="116" height="15" fontName="Times New Roman" fontSize="12.0">
|
||||
<expression><![CDATA[$P{checkupDate}]]></expression>
|
||||
</element>
|
||||
<element kind="staticText" uuid="9f7de81c-4c38-41a2-9fea-a165eff19431" x="1" y="124" width="59" height="16" fontName="Times New Roman" fontSize="10.0" bold="true" hTextAlign="Left">
|
||||
<element kind="staticText" uuid="9f7de81c-4c38-41a2-9fea-a165eff19431" x="1" y="116" width="69" height="16" fontName="Times New Roman" fontSize="12.0" bold="true" hTextAlign="Left">
|
||||
<text><![CDATA[Chẩn đoán:]]></text>
|
||||
</element>
|
||||
<element kind="textField" uuid="5ae67e13-7a79-437f-bfb8-c720d896720d" x="60" y="124" width="338" height="26" fontName="Times New Roman" fontSize="10.0">
|
||||
<element kind="textField" uuid="5ae67e13-7a79-437f-bfb8-c720d896720d" x="62" y="117" width="335" height="26" fontName="Times New Roman" fontSize="12.0">
|
||||
<expression><![CDATA[$P{patientDiagnos}]]></expression>
|
||||
</element>
|
||||
<element kind="staticText" uuid="b1fa845a-48da-4e45-82b8-8b9d972650f6" x="0" y="162" width="100" height="30" forecolor="#FF0905" fontName="Times New Roman" fontSize="14.0" bold="true">
|
||||
@@ -108,31 +102,31 @@
|
||||
<element kind="textField" uuid="6d652a25-b701-45bd-91ba-750842650004" x="0" y="177" width="398" height="376" markup="rtf" fontName="Times New Roman" fontSize="16.0">
|
||||
<expression><![CDATA[$P{checkupNote}]]></expression>
|
||||
</element>
|
||||
<element kind="staticText" uuid="8f720f6a-0257-4556-b3bc-3b81ad4f8099" x="290" y="150" width="59" height="16" fontName="Times New Roman" fontSize="10.0" bold="true" hTextAlign="Left">
|
||||
<element kind="staticText" uuid="8f720f6a-0257-4556-b3bc-3b81ad4f8099" x="297" y="145" width="59" height="16" fontName="Times New Roman" fontSize="12.0" bold="true" hTextAlign="Left">
|
||||
<text><![CDATA[Chiều cao:]]></text>
|
||||
</element>
|
||||
<element kind="staticText" uuid="a57e7a9d-4ac7-4219-b268-cd08ce943327" x="100" y="150" width="59" height="16" fontName="Times New Roman" fontSize="10.0" bold="true" hTextAlign="Left">
|
||||
<element kind="staticText" uuid="a57e7a9d-4ac7-4219-b268-cd08ce943327" x="124" y="145" width="59" height="16" fontName="Times New Roman" fontSize="12.0" bold="true" hTextAlign="Left">
|
||||
<text><![CDATA[Cân nặng:]]></text>
|
||||
</element>
|
||||
<element kind="staticText" uuid="2b9cfd9d-ec8f-4036-9c96-039fd7855e24" x="200" y="150" width="59" height="16" fontName="Times New Roman" fontSize="10.0" bold="true" hTextAlign="Left">
|
||||
<element kind="staticText" uuid="2b9cfd9d-ec8f-4036-9c96-039fd7855e24" x="217" y="145" width="59" height="16" fontName="Times New Roman" fontSize="12.0" bold="true" hTextAlign="Left">
|
||||
<text><![CDATA[Nhịp tim:]]></text>
|
||||
</element>
|
||||
<element kind="staticText" uuid="c1356370-11ce-4b68-97d6-ce6e18e306c5" x="0" y="150" width="51" height="16" fontName="Times New Roman" fontSize="10.0" bold="true" hTextAlign="Left">
|
||||
<element kind="staticText" uuid="c1356370-11ce-4b68-97d6-ce6e18e306c5" x="0" y="145" width="51" height="16" fontName="Times New Roman" fontSize="12.0" bold="true" hTextAlign="Left">
|
||||
<text><![CDATA[Huyết áp:]]></text>
|
||||
</element>
|
||||
<element kind="textField" uuid="3e1069a6-daea-45c2-b21f-935b49baba45" x="340" y="150" width="58" height="16" fontName="Times New Roman" fontSize="10.0">
|
||||
<expression><![CDATA[$P{checkupWeight} +" cm"]]></expression>
|
||||
<element kind="textField" uuid="3e1069a6-daea-45c2-b21f-935b49baba45" x="352" y="145" width="46" height="16" fontName="Times New Roman" fontSize="12.0">
|
||||
<expression><![CDATA[$P{checkupHeight} +" cm"]]></expression>
|
||||
</element>
|
||||
<element kind="textField" uuid="fb080012-3196-4535-bcd2-e87274c66fa5" x="147" y="150" width="53" height="16" fontName="Times New Roman" fontSize="10.0">
|
||||
<element kind="textField" uuid="fb080012-3196-4535-bcd2-e87274c66fa5" x="178" y="145" width="43" height="16" fontName="Times New Roman" fontSize="12.0">
|
||||
<expression><![CDATA[$P{checkupWeight} + " kg"]]></expression>
|
||||
</element>
|
||||
<element kind="textField" uuid="e4185ffe-8712-41e3-a9ff-2be6569dff15" x="243" y="150" width="47" height="16" fontName="Times New Roman" fontSize="10.0">
|
||||
<element kind="textField" uuid="e4185ffe-8712-41e3-a9ff-2be6569dff15" x="267" y="145" width="47" height="16" fontName="Times New Roman" fontSize="12.0">
|
||||
<expression><![CDATA[$P{checkupHeartBeat} + " l/p"]]></expression>
|
||||
</element>
|
||||
<element kind="textField" uuid="6b0b21be-2ca3-4594-bbe9-34868e13eb1e" x="45" y="150" width="55" height="16" fontName="Times New Roman" fontSize="10.0">
|
||||
<element kind="textField" uuid="6b0b21be-2ca3-4594-bbe9-34868e13eb1e" x="51" y="145" width="89" height="16" fontName="Times New Roman" fontSize="12.0">
|
||||
<expression><![CDATA[$P{checkupBloodPressure} +" mmHg"]]></expression>
|
||||
</element>
|
||||
<element kind="textField" uuid="9763a634-05c2-4f64-a903-c914455b7ee6" x="70" y="60" width="282" height="22" forecolor="#FF0905" fontName="Times New Roman" fontSize="12.0" bold="true" hTextAlign="Center">
|
||||
<element kind="textField" uuid="9763a634-05c2-4f64-a903-c914455b7ee6" x="3" y="30" width="340" height="22" forecolor="#FF0905" fontName="Times New Roman" fontSize="16.0" bold="true" hTextAlign="Center">
|
||||
<printWhenExpression><![CDATA[$P{templateTitle} != null && !$P{templateTitle}.trim().isEmpty()]]></printWhenExpression>
|
||||
<expression><![CDATA["PHIẾU " + $P{templateTitle}]]></expression>
|
||||
</element>
|
||||
@@ -249,14 +243,14 @@
|
||||
<expression><![CDATA[$P{image1}]]></expression>
|
||||
</element>
|
||||
</element>
|
||||
<element kind="staticText" uuid="8f35aa72-22a4-45f0-a35e-8e35e832404e" x="-48" y="54" width="44" height="21" fontName="Times New Roman" fontSize="8.0" bold="true" hTextAlign="Center">
|
||||
<element kind="staticText" uuid="8f35aa72-22a4-45f0-a35e-8e35e832404e" x="-68" y="51" width="66" height="21" fontName="Times New Roman" fontSize="8.0" bold="true" hTextAlign="Right">
|
||||
<printWhenExpression><![CDATA[$P{driveURL} != null && !$P{driveURL}.trim().isEmpty()]]></printWhenExpression>
|
||||
<text><![CDATA[Xem KQ online]]></text>
|
||||
</element>
|
||||
<element kind="textField" uuid="6d7df7cd-924f-4306-bcea-d643f7a977f0" x="-111" y="42" width="60" height="12" fontName="Times New Roman" fontSize="8.0" hTextAlign="Center">
|
||||
<element kind="textField" uuid="6d7df7cd-924f-4306-bcea-d643f7a977f0" x="-117" y="23" width="60" height="12" fontName="Times New Roman" fontSize="8.0" hTextAlign="Center">
|
||||
<expression><![CDATA[$P{id}]]></expression>
|
||||
</element>
|
||||
<element kind="image" uuid="16aab6b9-7618-451e-ae48-8bf7bd857bbc" x="-111" y="13" width="61" height="29">
|
||||
<element kind="image" uuid="16aab6b9-7618-451e-ae48-8bf7bd857bbc" x="-118" y="0" width="61" height="23">
|
||||
<expression><![CDATA[$P{barcodeNumber}]]></expression>
|
||||
</element>
|
||||
<element kind="image" uuid="14220495-3e07-4322-9c5f-ebf98a8083c8" x="-50" y="3" width="48" height="48">
|
||||
|
||||
@@ -56,55 +56,49 @@
|
||||
<element kind="textField" uuid="eb56d4c8-2a0d-45fa-89a2-ad6528f9a99b" x="1" y="-5" width="398" height="22" forecolor="#FF0905" fontName="Times New Roman" fontSize="14.0" bold="true" hTextAlign="Center">
|
||||
<expression><![CDATA[$P{clinicPrefix}]]></expression>
|
||||
</element>
|
||||
<element kind="textField" uuid="796d9ffd-6d2e-4ae3-bdfb-b08183d7d9c0" x="1" y="34" width="398" height="12" fontName="Times New Roman" fontSize="10.0" bold="true">
|
||||
<expression><![CDATA["Địa chỉ: " + $P{clinicAddress}]]></expression>
|
||||
</element>
|
||||
<element kind="textField" uuid="260bb33d-1d11-4178-aceb-3fbf8fcb4b45" x="1" y="46" width="398" height="15" fontName="Times New Roman" fontSize="8.0" bold="true">
|
||||
<expression><![CDATA["SĐT: " + $P{clinicPhone}]]></expression>
|
||||
</element>
|
||||
<element kind="staticText" uuid="6f89a93f-2d10-4506-baa5-226bdd047b17" x="0" y="65" width="100" height="30" forecolor="#FF0905" fontName="Times New Roman" fontSize="14.0" bold="true">
|
||||
<element kind="staticText" uuid="6f89a93f-2d10-4506-baa5-226bdd047b17" x="0" y="52" width="100" height="30" forecolor="#FF0905" fontName="Times New Roman" fontSize="14.0" bold="true">
|
||||
<text><![CDATA[I.Thông tin]]></text>
|
||||
</element>
|
||||
<element kind="staticText" uuid="b8dfca43-cf97-49e5-b58d-4667143dba62" x="0" y="79" width="59" height="15" fontName="Times New Roman" fontSize="12.0" bold="true" hTextAlign="Left">
|
||||
<element kind="staticText" uuid="b8dfca43-cf97-49e5-b58d-4667143dba62" x="0" y="67" width="59" height="15" fontName="Times New Roman" fontSize="12.0" bold="true" hTextAlign="Left">
|
||||
<text><![CDATA[Họ và tên:]]></text>
|
||||
</element>
|
||||
<element kind="staticText" uuid="0a9ca0c8-f2bd-4ade-a025-a6d79c13613f" x="1" y="94" width="80" height="15" fontName="Times New Roman" fontSize="12.0" bold="true" hTextAlign="Left">
|
||||
<element kind="staticText" uuid="0a9ca0c8-f2bd-4ade-a025-a6d79c13613f" x="1" y="83" width="80" height="15" fontName="Times New Roman" fontSize="12.0" bold="true" hTextAlign="Left">
|
||||
<text><![CDATA[Ngày sinh:]]></text>
|
||||
</element>
|
||||
<element kind="staticText" uuid="21917ab8-ae07-44c8-ba4e-bc6c4c54b440" x="130" y="95" width="80" height="15" fontName="Times New Roman" fontSize="12.0" bold="true" hTextAlign="Left">
|
||||
<element kind="staticText" uuid="21917ab8-ae07-44c8-ba4e-bc6c4c54b440" x="130" y="84" width="80" height="15" fontName="Times New Roman" fontSize="12.0" bold="true" hTextAlign="Left">
|
||||
<text><![CDATA[Giới tính:]]></text>
|
||||
</element>
|
||||
<element kind="staticText" uuid="bcbb614e-7a4d-48a3-83c2-198e4fb62c5f" x="1" y="109" width="49" height="15" fontName="Times New Roman" fontSize="12.0" bold="true" hTextAlign="Left">
|
||||
<element kind="staticText" uuid="bcbb614e-7a4d-48a3-83c2-198e4fb62c5f" x="1" y="101" width="49" height="15" fontName="Times New Roman" fontSize="12.0" bold="true" hTextAlign="Left">
|
||||
<text><![CDATA[Địa chỉ:]]></text>
|
||||
</element>
|
||||
<element kind="staticText" uuid="45fe4be1-9e21-459b-aeee-2efc8354f372" x="221" y="80" width="89" height="15" fontName="Times New Roman" fontSize="12.0" bold="true" hTextAlign="Left">
|
||||
<element kind="staticText" uuid="45fe4be1-9e21-459b-aeee-2efc8354f372" x="221" y="67" width="89" height="15" fontName="Times New Roman" fontSize="12.0" bold="true" hTextAlign="Left">
|
||||
<text><![CDATA[Bác sĩ chỉ định:]]></text>
|
||||
</element>
|
||||
<element kind="staticText" uuid="f2ff4b17-63cc-4e8c-a3b0-2523f534f21a" x="221" y="94" width="69" height="15" fontName="Times New Roman" fontSize="12.0" bold="true" hTextAlign="Left">
|
||||
<element kind="staticText" uuid="f2ff4b17-63cc-4e8c-a3b0-2523f534f21a" x="221" y="83" width="69" height="15" fontName="Times New Roman" fontSize="12.0" bold="true" hTextAlign="Left">
|
||||
<text><![CDATA[Ngày khám:]]></text>
|
||||
</element>
|
||||
<element kind="textField" uuid="1511f53a-b282-41b1-b233-612b6106c07a" x="56" y="79" width="165" height="15" fontName="Times New Roman" fontSize="12.0">
|
||||
<element kind="textField" uuid="1511f53a-b282-41b1-b233-612b6106c07a" x="56" y="67" width="165" height="15" fontName="Times New Roman" fontSize="12.0">
|
||||
<expression><![CDATA[$P{patientName}]]></expression>
|
||||
</element>
|
||||
<element kind="textField" uuid="5bdac267-523d-4931-926b-76233ce9b84d" x="60" y="93" width="70" height="15" fontName="Times New Roman" fontSize="12.0">
|
||||
<element kind="textField" uuid="5bdac267-523d-4931-926b-76233ce9b84d" x="60" y="83" width="70" height="15" fontName="Times New Roman" fontSize="12.0">
|
||||
<expression><![CDATA[$P{patientDOB}]]></expression>
|
||||
</element>
|
||||
<element kind="textField" uuid="f4df20b4-920c-491e-882b-6e0733cb4866" x="185" y="95" width="36" height="15" fontName="Times New Roman" fontSize="12.0">
|
||||
<element kind="textField" uuid="f4df20b4-920c-491e-882b-6e0733cb4866" x="185" y="84" width="36" height="15" fontName="Times New Roman" fontSize="12.0">
|
||||
<expression><![CDATA[$P{patientGender}]]></expression>
|
||||
</element>
|
||||
<element kind="textField" uuid="3b8ac40e-85aa-4ab8-8445-64bcddb8b267" x="50" y="109" width="350" height="15" fontName="Times New Roman" fontSize="12.0">
|
||||
<element kind="textField" uuid="3b8ac40e-85aa-4ab8-8445-64bcddb8b267" x="50" y="101" width="350" height="15" fontName="Times New Roman" fontSize="12.0">
|
||||
<expression><![CDATA[$P{patientAddress}]]></expression>
|
||||
</element>
|
||||
<element kind="textField" uuid="db40cdb0-2850-4d66-8430-addc54f90321" x="300" y="80" width="110" height="15" fontName="Times New Roman" fontSize="12.0">
|
||||
<element kind="textField" uuid="db40cdb0-2850-4d66-8430-addc54f90321" x="301" y="67" width="128" height="15" fontName="Times New Roman" fontSize="12.0">
|
||||
<expression><![CDATA[$P{doctorName}]]></expression>
|
||||
</element>
|
||||
<element kind="textField" uuid="eb9cf54a-3b50-43c5-8696-cfc1e21bddfe" x="290" y="94" width="110" height="15" fontName="Times New Roman" fontSize="12.0">
|
||||
<element kind="textField" uuid="eb9cf54a-3b50-43c5-8696-cfc1e21bddfe" x="290" y="83" width="110" height="15" fontName="Times New Roman" fontSize="12.0">
|
||||
<expression><![CDATA[$P{checkupDate}]]></expression>
|
||||
</element>
|
||||
<element kind="staticText" uuid="9f7de81c-4c38-41a2-9fea-a165eff19431" x="1" y="124" width="69" height="16" fontName="Times New Roman" fontSize="12.0" bold="true" hTextAlign="Left">
|
||||
<element kind="staticText" uuid="9f7de81c-4c38-41a2-9fea-a165eff19431" x="1" y="116" width="69" height="16" fontName="Times New Roman" fontSize="12.0" bold="true" hTextAlign="Left">
|
||||
<text><![CDATA[Chẩn đoán:]]></text>
|
||||
</element>
|
||||
<element kind="textField" uuid="5ae67e13-7a79-437f-bfb8-c720d896720d" x="70" y="124" width="329" height="26" fontName="Times New Roman" fontSize="12.0">
|
||||
<element kind="textField" uuid="5ae67e13-7a79-437f-bfb8-c720d896720d" x="70" y="116" width="329" height="26" fontName="Times New Roman" fontSize="12.0">
|
||||
<expression><![CDATA[$P{patientDiagnos}]]></expression>
|
||||
</element>
|
||||
<element kind="staticText" uuid="b1fa845a-48da-4e45-82b8-8b9d972650f6" x="0" y="162" width="100" height="30" forecolor="#FF0905" fontName="Times New Roman" fontSize="14.0" bold="true">
|
||||
@@ -113,31 +107,31 @@
|
||||
<element kind="textField" uuid="6d652a25-b701-45bd-91ba-750842650004" x="0" y="177" width="400" height="421" markup="rtf" fontName="Times New Roman" fontSize="16.0">
|
||||
<expression><![CDATA[$P{checkupNote}]]></expression>
|
||||
</element>
|
||||
<element kind="staticText" uuid="8f720f6a-0257-4556-b3bc-3b81ad4f8099" x="300" y="150" width="59" height="16" fontName="Times New Roman" fontSize="12.0" bold="true" hTextAlign="Left">
|
||||
<element kind="staticText" uuid="8f720f6a-0257-4556-b3bc-3b81ad4f8099" x="297" y="150" width="59" height="16" fontName="Times New Roman" fontSize="12.0" bold="true" hTextAlign="Left">
|
||||
<text><![CDATA[Chiều cao:]]></text>
|
||||
</element>
|
||||
<element kind="staticText" uuid="a57e7a9d-4ac7-4219-b268-cd08ce943327" x="110" y="150" width="59" height="16" fontName="Times New Roman" fontSize="12.0" bold="true" hTextAlign="Left">
|
||||
<element kind="staticText" uuid="a57e7a9d-4ac7-4219-b268-cd08ce943327" x="123" y="150" width="59" height="16" fontName="Times New Roman" fontSize="12.0" bold="true" hTextAlign="Left">
|
||||
<text><![CDATA[Cân nặng:]]></text>
|
||||
</element>
|
||||
<element kind="staticText" uuid="2b9cfd9d-ec8f-4036-9c96-039fd7855e24" x="210" y="150" width="59" height="16" fontName="Times New Roman" fontSize="12.0" bold="true" hTextAlign="Left">
|
||||
<element kind="staticText" uuid="2b9cfd9d-ec8f-4036-9c96-039fd7855e24" x="215" y="150" width="59" height="16" fontName="Times New Roman" fontSize="12.0" bold="true" hTextAlign="Left">
|
||||
<text><![CDATA[Nhịp tim:]]></text>
|
||||
</element>
|
||||
<element kind="staticText" uuid="c1356370-11ce-4b68-97d6-ce6e18e306c5" x="0" y="150" width="59" height="16" fontName="Times New Roman" fontSize="12.0" bold="true" hTextAlign="Left">
|
||||
<text><![CDATA[Huyết áp:]]></text>
|
||||
</element>
|
||||
<element kind="textField" uuid="3e1069a6-daea-45c2-b21f-935b49baba45" x="356" y="150" width="44" height="16" fontName="Times New Roman" fontSize="12.0">
|
||||
<expression><![CDATA[$P{checkupWeight} +" cm"]]></expression>
|
||||
<element kind="textField" uuid="3e1069a6-daea-45c2-b21f-935b49baba45" x="352" y="150" width="53" height="16" fontName="Times New Roman" fontSize="12.0">
|
||||
<expression><![CDATA[$P{checkupHeight} +" cm"]]></expression>
|
||||
</element>
|
||||
<element kind="textField" uuid="fb080012-3196-4535-bcd2-e87274c66fa5" x="165" y="150" width="45" height="16" fontName="Times New Roman" fontSize="12.0">
|
||||
<element kind="textField" uuid="fb080012-3196-4535-bcd2-e87274c66fa5" x="178" y="150" width="45" height="16" fontName="Times New Roman" fontSize="12.0">
|
||||
<expression><![CDATA[$P{checkupWeight} + " kg"]]></expression>
|
||||
</element>
|
||||
<element kind="textField" uuid="e4185ffe-8712-41e3-a9ff-2be6569dff15" x="261" y="150" width="39" height="16" fontName="Times New Roman" fontSize="12.0">
|
||||
<element kind="textField" uuid="e4185ffe-8712-41e3-a9ff-2be6569dff15" x="266" y="150" width="39" height="16" fontName="Times New Roman" fontSize="12.0">
|
||||
<expression><![CDATA[$P{checkupHeartBeat} + " l/p"]]></expression>
|
||||
</element>
|
||||
<element kind="textField" uuid="6b0b21be-2ca3-4594-bbe9-34868e13eb1e" x="52" y="150" width="58" height="16" fontName="Times New Roman" fontSize="12.0">
|
||||
<element kind="textField" uuid="6b0b21be-2ca3-4594-bbe9-34868e13eb1e" x="52" y="150" width="78" height="16" fontName="Times New Roman" fontSize="12.0">
|
||||
<expression><![CDATA[$P{checkupBloodPressure} +" mmHg"]]></expression>
|
||||
</element>
|
||||
<element kind="textField" uuid="9763a634-05c2-4f64-a903-c914455b7ee6" x="70" y="61" width="282" height="20" forecolor="#FF0905" fontName="Times New Roman" fontSize="12.0" bold="true" hTextAlign="Center">
|
||||
<element kind="textField" uuid="9763a634-05c2-4f64-a903-c914455b7ee6" x="1" y="32" width="398" height="26" forecolor="#FF0905" fontName="Times New Roman" fontSize="16.0" bold="true" hTextAlign="Center">
|
||||
<printWhenExpression><![CDATA[$P{templateTitle} != null && !$P{templateTitle}.trim().isEmpty()]]></printWhenExpression>
|
||||
<expression><![CDATA["PHIẾU " + $P{templateTitle}]]></expression>
|
||||
</element>
|
||||
@@ -163,10 +157,10 @@
|
||||
<element kind="image" uuid="a38cebd9-fec4-4e7f-8ec2-e6ee57a7464b" x="495" y="1" width="55" height="62">
|
||||
<expression><![CDATA[$P{driveURL}]]></expression>
|
||||
</element>
|
||||
<element kind="image" uuid="69654713-8c32-44f0-9d34-a2a7b9b2bb8d" x="404" y="8" width="80" height="34">
|
||||
<element kind="image" uuid="69654713-8c32-44f0-9d34-a2a7b9b2bb8d" x="401" y="0" width="80" height="34">
|
||||
<expression><![CDATA[$P{barcodeNumber}]]></expression>
|
||||
</element>
|
||||
<element kind="textField" uuid="989a8031-ea1b-406a-98a4-e797b98c0ccd" x="404" y="43" width="80" height="15" fontName="Times New Roman" fontSize="10.0" hTextAlign="Center">
|
||||
<element kind="textField" uuid="989a8031-ea1b-406a-98a4-e797b98c0ccd" x="405" y="34" width="80" height="15" fontName="Times New Roman" fontSize="10.0" hTextAlign="Center">
|
||||
<expression><![CDATA[$P{id}]]></expression>
|
||||
</element>
|
||||
</element>
|
||||
|
||||
|
Before Width: | Height: | Size: 103 B After Width: | Height: | Size: 102 B |
|
Before Width: | Height: | Size: 524 B After Width: | Height: | Size: 516 B |