更新
This commit is contained in:
@@ -8,9 +8,10 @@ from datetime import date, timedelta
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from PySide6.QtCore import Qt, QTimer, Signal
|
||||
from PySide6.QtGui import QTextCursor
|
||||
from PySide6.QtCore import Qt, QTimer, QUrl, Signal
|
||||
from PySide6.QtGui import QDesktopServices, QPixmap, QTextCursor
|
||||
from PySide6.QtWidgets import (
|
||||
QDialog,
|
||||
QFileDialog,
|
||||
QFrame,
|
||||
QGridLayout,
|
||||
@@ -22,6 +23,7 @@ from PySide6.QtWidgets import (
|
||||
QMessageBox,
|
||||
QPushButton,
|
||||
QScrollArea,
|
||||
QSizePolicy,
|
||||
QSplitter,
|
||||
QStackedWidget,
|
||||
QTabBar,
|
||||
@@ -118,6 +120,13 @@ def _attachment_name(value: object) -> str:
|
||||
return name or text
|
||||
|
||||
|
||||
def _is_image_attachment(value: object) -> bool:
|
||||
"""Return whether a server attachment can be previewed as an image."""
|
||||
|
||||
path = str(value or "").split("?", 1)[0].split("#", 1)[0].lower()
|
||||
return path.endswith((".jpg", ".jpeg", ".png", ".gif", ".bmp", ".webp"))
|
||||
|
||||
|
||||
def _is_local_material_reference(value: str) -> bool:
|
||||
"""Reject local filesystem references before a note JSON is constructed."""
|
||||
|
||||
@@ -139,12 +148,14 @@ class QueueRow(QWidget):
|
||||
layout.setContentsMargins(12, 9, 12, 9)
|
||||
layout.setSpacing(5)
|
||||
top = QHBoxLayout()
|
||||
top.setSpacing(8)
|
||||
name = QLabel(
|
||||
display_text(first_value(record, "patient_name", "name", default="未命名患者"))
|
||||
)
|
||||
name.setStyleSheet("font-size:14px; font-weight:700; color:#172033;")
|
||||
top.addWidget(name)
|
||||
top.addStretch(1)
|
||||
name.setMinimumWidth(0)
|
||||
name.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Preferred)
|
||||
top.addWidget(name, 1)
|
||||
status_number = _as_int(first_value(record, "status", default=1), 1) or 1
|
||||
badge = StatusBadge(
|
||||
display_text(
|
||||
@@ -157,7 +168,12 @@ class QueueRow(QWidget):
|
||||
),
|
||||
STATUS_KIND.get(status_number, "neutral"),
|
||||
)
|
||||
top.addWidget(badge)
|
||||
badge.setMinimumWidth(max(54, badge.sizeHint().width()))
|
||||
top.addWidget(
|
||||
badge,
|
||||
0,
|
||||
Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignTop,
|
||||
)
|
||||
layout.addLayout(top)
|
||||
time = first_value(
|
||||
record, "appointment_time_text", "appointment_time", "time", default="时间待确认"
|
||||
@@ -168,6 +184,10 @@ class QueueRow(QWidget):
|
||||
f"{display_text(first_value(record, 'age'))}岁"
|
||||
)
|
||||
meta.setProperty("role", "muted")
|
||||
# Long timestamps must not dictate the minimum width of the whole
|
||||
# queue row and push the status badge underneath the viewport edge.
|
||||
meta.setMinimumWidth(0)
|
||||
meta.setSizePolicy(QSizePolicy.Policy.Ignored, QSizePolicy.Policy.Preferred)
|
||||
layout.addWidget(meta)
|
||||
assistant = first_value(record, "assistant_name", default=None)
|
||||
if assistant:
|
||||
@@ -211,6 +231,7 @@ class ReceptionPage(QWidget):
|
||||
self._pending_tongue_images: list[str] = []
|
||||
self._pending_report_files: list[str] = []
|
||||
self._note_busy = False
|
||||
self._attachment_preview_generation = 0
|
||||
|
||||
self._can_complete = has_permission(self.permissions, "doctor.appointment/complete")
|
||||
self._can_note = has_permission(self.permissions, "doctor.appointment/addDoctorNote")
|
||||
@@ -335,7 +356,8 @@ class ReceptionPage(QWidget):
|
||||
self.notify_button = QPushButton("通知医助")
|
||||
self.notify_button.clicked.connect(self._notify_assistant)
|
||||
action_row.addWidget(self.notify_button)
|
||||
self.video_button = QPushButton("发起视频")
|
||||
self.video_button = QPushButton("IM 问诊")
|
||||
self.video_button.setToolTip("打开患者 IM,可发送消息并从会话中发起视频")
|
||||
self.video_button.setProperty("variant", "secondary")
|
||||
self.video_button.clicked.connect(self._request_video)
|
||||
action_row.addWidget(self.video_button)
|
||||
@@ -1245,13 +1267,25 @@ class ReceptionPage(QWidget):
|
||||
("report_files", "检查报告"),
|
||||
):
|
||||
for path in _sequence(first_value(note, image_type, default=[])):
|
||||
path_text = str(path).strip()
|
||||
attachment = QWidget()
|
||||
attachment_layout = QHBoxLayout(attachment)
|
||||
attachment_layout.setContentsMargins(0, 0, 0, 0)
|
||||
label = QLabel(f"{caption}:{_attachment_name(path)}")
|
||||
label.setToolTip(str(path))
|
||||
label = QLabel(f"{caption}:{_attachment_name(path_text)}")
|
||||
label.setToolTip(path_text)
|
||||
label.setTextInteractionFlags(Qt.TextInteractionFlag.TextSelectableByMouse)
|
||||
attachment_layout.addWidget(label, 1)
|
||||
preview_button = QPushButton(
|
||||
"预览" if _is_image_attachment(path_text) else "打开"
|
||||
)
|
||||
preview_button.setProperty("variant", "secondary")
|
||||
preview_button.setEnabled(bool(path_text))
|
||||
preview_button.clicked.connect(
|
||||
lambda _checked=False, current_path=path_text, button=preview_button: (
|
||||
self._preview_note_attachment(current_path, button)
|
||||
)
|
||||
)
|
||||
attachment_layout.addWidget(preview_button)
|
||||
if self._can_note and note_id is not None:
|
||||
delete_button = QPushButton("删除")
|
||||
delete_button.setProperty("variant", "secondary")
|
||||
@@ -1266,6 +1300,97 @@ class ReceptionPage(QWidget):
|
||||
layout.addWidget(attachment)
|
||||
self.notes_layout.addWidget(card)
|
||||
|
||||
def _preview_note_attachment(self, path: str, button: QPushButton) -> None:
|
||||
"""Preview note images in-app and open non-image reports safely."""
|
||||
|
||||
target = str(path or "").strip()
|
||||
if not target:
|
||||
return
|
||||
if not _is_image_attachment(target):
|
||||
url = QUrl(target)
|
||||
if (
|
||||
not url.isValid()
|
||||
or url.scheme().lower() not in {"https", "http"}
|
||||
or not url.host()
|
||||
):
|
||||
show_toast(self, "附件地址无效,无法打开。", "warning", 4200)
|
||||
return
|
||||
if not QDesktopServices.openUrl(url):
|
||||
show_toast(self, "系统未能打开该附件。", "danger", 4200)
|
||||
return
|
||||
|
||||
download = getattr(self.repository, "download_public_image", None)
|
||||
if not callable(download):
|
||||
show_toast(self, "当前数据源不支持图片预览。", "warning", 4200)
|
||||
return
|
||||
self._attachment_preview_generation += 1
|
||||
generation = self._attachment_preview_generation
|
||||
button.setEnabled(False)
|
||||
button.setText("加载中…")
|
||||
run_async(
|
||||
lambda: invoke(self.repository, "download_public_image", url=target),
|
||||
on_success=lambda payload: self._show_note_image_preview(
|
||||
target, payload, button, generation
|
||||
),
|
||||
on_error=lambda error: self._note_image_preview_failed(
|
||||
error, button, generation
|
||||
),
|
||||
)
|
||||
|
||||
def _show_note_image_preview(
|
||||
self,
|
||||
path: str,
|
||||
payload: Any,
|
||||
button: QPushButton,
|
||||
generation: int,
|
||||
) -> None:
|
||||
if generation != self._attachment_preview_generation:
|
||||
return
|
||||
button.setEnabled(True)
|
||||
button.setText("预览")
|
||||
content = bytes(payload or b"")
|
||||
pixmap = QPixmap()
|
||||
if not content or len(content) > 10 * 1024 * 1024 or not pixmap.loadFromData(content):
|
||||
show_toast(self, "服务器返回的图片无法预览。", "danger", 4600)
|
||||
return
|
||||
dialog = QDialog(self)
|
||||
dialog.setWindowTitle(f"预览 · {_attachment_name(path)}")
|
||||
dialog.setModal(True)
|
||||
dialog.resize(920, 680)
|
||||
layout = QVBoxLayout(dialog)
|
||||
layout.setContentsMargins(14, 14, 14, 14)
|
||||
scroll = QScrollArea(dialog)
|
||||
scroll.setWidgetResizable(True)
|
||||
image = QLabel()
|
||||
image.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
image.setPixmap(
|
||||
pixmap.scaled(
|
||||
880,
|
||||
620,
|
||||
Qt.AspectRatioMode.KeepAspectRatio,
|
||||
Qt.TransformationMode.SmoothTransformation,
|
||||
)
|
||||
)
|
||||
scroll.setWidget(image)
|
||||
layout.addWidget(scroll, 1)
|
||||
close_button = QPushButton("关闭")
|
||||
close_button.setProperty("variant", "primary")
|
||||
close_button.clicked.connect(dialog.accept)
|
||||
layout.addWidget(close_button, 0, Qt.AlignmentFlag.AlignRight)
|
||||
dialog.exec()
|
||||
|
||||
def _note_image_preview_failed(
|
||||
self,
|
||||
error: Exception,
|
||||
button: QPushButton,
|
||||
generation: int,
|
||||
) -> None:
|
||||
if generation != self._attachment_preview_generation:
|
||||
return
|
||||
button.setEnabled(True)
|
||||
button.setText("预览")
|
||||
show_toast(self, f"图片预览失败:{friendly_error(error)}", "danger", 5200)
|
||||
|
||||
def _reset_detail_content(self, seed: Any = None) -> None:
|
||||
self.patient_name_label.setText(
|
||||
display_text(first_value(seed, "patient_name", "name", default="—"))
|
||||
@@ -1753,6 +1878,7 @@ class ReceptionPage(QWidget):
|
||||
"patient_name",
|
||||
default=first_value(diagnosis, "patient_name", default="患者"),
|
||||
),
|
||||
"mode": "im",
|
||||
"record": self._selected_record,
|
||||
}
|
||||
self.video_requested.emit(payload)
|
||||
|
||||
Reference in New Issue
Block a user