更新bug
This commit is contained in:
@@ -9,8 +9,8 @@ from html import escape
|
||||
from types import MappingProxyType
|
||||
from typing import Any
|
||||
|
||||
from PySide6.QtCore import QDate, QSize, Qt, QTimer, QUrl, Signal
|
||||
from PySide6.QtGui import QAction, QBrush, QColor, QDesktopServices, QPixmap
|
||||
from PySide6.QtCore import QDate, Qt, QTimer, QUrl, Signal
|
||||
from PySide6.QtGui import QBrush, QColor, QDesktopServices, QPixmap
|
||||
from PySide6.QtWidgets import (
|
||||
QAbstractItemView,
|
||||
QButtonGroup,
|
||||
@@ -21,10 +21,9 @@ from PySide6.QtWidgets import (
|
||||
QDialogButtonBox,
|
||||
QFrame,
|
||||
QHBoxLayout,
|
||||
QHeaderView,
|
||||
QLabel,
|
||||
QLineEdit,
|
||||
QListWidget,
|
||||
QListWidgetItem,
|
||||
QMenu,
|
||||
QMessageBox,
|
||||
QPushButton,
|
||||
@@ -104,8 +103,7 @@ APPOINTMENTS_LIGHT_QSS = """
|
||||
border: 1px solid #E2E7F4;
|
||||
border-radius: 10px;
|
||||
}
|
||||
#AppointmentsPage QFrame#AppointmentMainCard,
|
||||
#AppointmentsPage QFrame#VideoConsultPanel {
|
||||
#AppointmentsPage QFrame#AppointmentMainCard {
|
||||
background-color: #FFFFFF;
|
||||
border: 1px solid #E2E7F4;
|
||||
border-radius: 10px;
|
||||
@@ -209,6 +207,34 @@ APPOINTMENTS_LIGHT_QSS = """
|
||||
border-radius: 4px;
|
||||
font-size: 10px;
|
||||
}
|
||||
#AppointmentsPage QWidget[appointmentImHost="true"] {
|
||||
background-color: transparent;
|
||||
}
|
||||
#AppointmentsPage QPushButton[appointmentImAction="true"] {
|
||||
min-width: 74px;
|
||||
min-height: 26px;
|
||||
max-height: 26px;
|
||||
padding: 0 9px;
|
||||
color: #5265F6;
|
||||
background-color: #F0F2FF;
|
||||
border: 1px solid #D7DEFF;
|
||||
border-radius: 7px;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
}
|
||||
#AppointmentsPage QPushButton[appointmentImAction="true"]:hover {
|
||||
color: #FFFFFF;
|
||||
background-color: #5265F6;
|
||||
border-color: #5265F6;
|
||||
}
|
||||
#AppointmentsPage QPushButton[appointmentImAction="true"]:focus {
|
||||
border-color: #8D9BFF;
|
||||
}
|
||||
#AppointmentsPage QPushButton[appointmentImAction="true"]:disabled {
|
||||
color: #98A3BC;
|
||||
background-color: #F7F8FC;
|
||||
border-color: #E4E8F2;
|
||||
}
|
||||
#AppointmentsPage QWidget[appointmentInfoHost="true"] {
|
||||
background-color: #FFFFFF;
|
||||
}
|
||||
@@ -257,77 +283,6 @@ APPOINTMENTS_LIGHT_QSS = """
|
||||
background-color: #EEF1FF;
|
||||
border-bottom-color: #5265F6;
|
||||
}
|
||||
#AppointmentsPage QLabel#VideoConsultTitle {
|
||||
color: #15224A;
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
}
|
||||
#AppointmentsPage QListWidget#VideoConsultList {
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
outline: 0;
|
||||
}
|
||||
#AppointmentsPage QListWidget#VideoConsultList::item {
|
||||
min-height: 80px;
|
||||
margin: 0 0 7px 0;
|
||||
padding: 0;
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
}
|
||||
#AppointmentsPage QListWidget#VideoConsultList::item:hover {
|
||||
background-color: transparent;
|
||||
}
|
||||
#AppointmentsPage QListWidget#VideoConsultList::item:selected {
|
||||
background-color: transparent;
|
||||
}
|
||||
#AppointmentsPage QFrame[videoPatientCard="true"] {
|
||||
background-color: #FCFDFF;
|
||||
border: 1px solid #E1E6F1;
|
||||
border-radius: 9px;
|
||||
}
|
||||
#AppointmentsPage QFrame[videoPatientCard="true"]:hover {
|
||||
background-color: #F9FAFF;
|
||||
border-color: #C9D2FF;
|
||||
}
|
||||
#AppointmentsPage QLabel[videoStatus="true"] {
|
||||
min-height: 20px;
|
||||
max-height: 20px;
|
||||
padding: 0 7px;
|
||||
color: #5265F6;
|
||||
background-color: #EEF1FF;
|
||||
border-radius: 5px;
|
||||
font-size: 10px;
|
||||
font-weight: 600;
|
||||
}
|
||||
#AppointmentsPage QLabel[videoStatusKind="warning"] {
|
||||
color: #B97715;
|
||||
background-color: #FFF4DF;
|
||||
}
|
||||
#AppointmentsPage QLabel[videoIdentity="true"] {
|
||||
color: #24335E;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
}
|
||||
#AppointmentsPage QLabel[videoAssistant="true"] {
|
||||
color: #59698E;
|
||||
font-size: 11px;
|
||||
}
|
||||
#AppointmentsPage QPushButton[videoAction="true"] {
|
||||
min-height: 20px;
|
||||
max-height: 20px;
|
||||
padding: 0 5px;
|
||||
color: #5265F6;
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
border-radius: 4px;
|
||||
font-size: 10px;
|
||||
}
|
||||
#AppointmentsPage QPushButton[videoAction="true"]:hover {
|
||||
background-color: #EEF1FF;
|
||||
}
|
||||
#AppointmentsPage QPushButton[videoActionKind="success"] { color: #159C79; }
|
||||
#AppointmentsPage QPushButton[videoActionKind="warning"] { color: #C17A16; }
|
||||
#AppointmentsPage QPushButton[filterChoice="true"] {
|
||||
min-height: 28px;
|
||||
max-height: 28px;
|
||||
@@ -342,11 +297,6 @@ APPOINTMENTS_LIGHT_QSS = """
|
||||
background-color: #F0F1FF;
|
||||
border-color: #E0E4FF;
|
||||
}
|
||||
#AppointmentsPage QPushButton#VideoMoreButton {
|
||||
min-height: 36px;
|
||||
color: #405074;
|
||||
background-color: #FFFFFF;
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
@@ -452,14 +402,37 @@ def _status_value(row: Any) -> int:
|
||||
return _as_int(first_value(row, "status", default=0))
|
||||
|
||||
|
||||
_IM_CONSULT_ACTIVE_STATUSES = frozenset({1, 4})
|
||||
|
||||
|
||||
def _im_consult_status_error(row: Any) -> str:
|
||||
"""Return why a queue row cannot open the doctor's IM consultation."""
|
||||
|
||||
status = _status_value(row)
|
||||
if status in _IM_CONSULT_ACTIVE_STATUSES:
|
||||
return ""
|
||||
if status == 2:
|
||||
return "已取消的挂号不可进入 IM 问诊"
|
||||
if status == 3:
|
||||
return "已完成的挂号不可再进入 IM 问诊"
|
||||
return "当前挂号状态不可进入 IM 问诊"
|
||||
|
||||
|
||||
def prescription_action_label(row: Any) -> str:
|
||||
audit = _as_int(first_value(row, "prescription_audit_status", "audit_status"), -1)
|
||||
voided = _as_int(first_value(row, "prescription_void_status", "void_status"), 0)
|
||||
explicit = first_value(row, "has_prescription", default=None)
|
||||
explicit = first_value(row, "current_has_prescription", default=None)
|
||||
if explicit is None and _as_int(first_value(row, "current_prescription_id", default=0)) <= 0:
|
||||
explicit = first_value(row, "has_prescription", default=None)
|
||||
has_prescription = (
|
||||
_as_bool(explicit)
|
||||
if explicit is not None
|
||||
else _as_int(first_value(row, "prescription_id", default=0), 0) > 0 or audit in {0, 1, 2}
|
||||
else _as_int(
|
||||
first_value(row, "current_prescription_id", "prescription_id", default=0),
|
||||
0,
|
||||
)
|
||||
> 0
|
||||
or audit in {0, 1, 2}
|
||||
)
|
||||
if not has_prescription:
|
||||
return "开方"
|
||||
@@ -689,7 +662,6 @@ class AppointmentsPage(QWidget):
|
||||
self._dict_labels: dict[str, dict[str, str]] = {}
|
||||
self._last_render_signature: tuple[Any, ...] | None = None
|
||||
self._responsive_narrow: bool | None = None
|
||||
self._video_panel_user_open = False
|
||||
self._can_plain_phone = _canonical_allowed(
|
||||
permissions, "tcm.diagnosis/phonePlain", default=False
|
||||
)
|
||||
@@ -889,35 +861,9 @@ class AppointmentsPage(QWidget):
|
||||
layout.setSpacing(4)
|
||||
self.content_layout = layout
|
||||
self.table_card = self._build_table_card()
|
||||
self.video_panel = self._build_video_panel()
|
||||
layout.addWidget(self.table_card, 3)
|
||||
layout.addWidget(self.video_panel, 1)
|
||||
layout.addWidget(self.table_card, 1)
|
||||
return host
|
||||
|
||||
def _build_video_panel(self) -> QWidget:
|
||||
panel = QFrame()
|
||||
panel.setObjectName("VideoConsultPanel")
|
||||
panel.setMinimumWidth(0)
|
||||
panel.setMaximumWidth(420)
|
||||
layout = QVBoxLayout(panel)
|
||||
layout.setContentsMargins(14, 12, 14, 8)
|
||||
layout.setSpacing(8)
|
||||
title = QLabel("视频问诊")
|
||||
title.setObjectName("VideoConsultTitle")
|
||||
layout.addWidget(title)
|
||||
self.video_list = QListWidget()
|
||||
self.video_list.setObjectName("VideoConsultList")
|
||||
self.video_list.setSpacing(0)
|
||||
self.video_list.setVerticalScrollMode(QAbstractItemView.ScrollMode.ScrollPerPixel)
|
||||
self.video_list.currentRowChanged.connect(self._video_row_selected)
|
||||
self.video_list.itemDoubleClicked.connect(lambda _item: self._open_detail())
|
||||
layout.addWidget(self.video_list, 1)
|
||||
more = QPushButton("查看更多患者 ﹀")
|
||||
more.setObjectName("VideoMoreButton")
|
||||
more.clicked.connect(lambda: self.pager.next.click())
|
||||
layout.addWidget(more)
|
||||
return panel
|
||||
|
||||
def _build_table_card(self) -> QWidget:
|
||||
card = QFrame()
|
||||
card.setObjectName("AppointmentMainCard")
|
||||
@@ -1004,12 +950,6 @@ class AppointmentsPage(QWidget):
|
||||
self.toolbar_cancel_button.setEnabled(False)
|
||||
self.toolbar_cancel_button.clicked.connect(self._cancel_selected)
|
||||
actions.addWidget(self.toolbar_cancel_button)
|
||||
self.video_panel_button = QPushButton("视频问诊", card)
|
||||
self.video_panel_button.setCheckable(True)
|
||||
self.video_panel_button.setProperty("variant", "ghost")
|
||||
self.video_panel_button.setProperty("compactAction", True)
|
||||
self.video_panel_button.clicked.connect(self._toggle_video_panel)
|
||||
actions.addWidget(self.video_panel_button)
|
||||
actions.addStretch(1)
|
||||
refresh = QPushButton("刷新", card)
|
||||
refresh.setProperty("variant", "ghost")
|
||||
@@ -1034,13 +974,23 @@ class AppointmentsPage(QWidget):
|
||||
TableColumn("assistant_name", "助理", 110),
|
||||
TableColumn("has_prescription", "开方", 108, formatter=_prescription_cell),
|
||||
TableColumn("unserved_days", "未服务天数", 112, formatter=_unserved_cell),
|
||||
TableColumn(
|
||||
"_im_consult",
|
||||
"IM 问诊",
|
||||
104,
|
||||
alignment=Qt.AlignmentFlag.AlignCenter,
|
||||
),
|
||||
]
|
||||
)
|
||||
self.table.setObjectName("AppointmentTable")
|
||||
self.table.setMinimumHeight(0)
|
||||
self.table.setWordWrap(True)
|
||||
self.table.setVerticalScrollMode(QAbstractItemView.ScrollMode.ScrollPerPixel)
|
||||
self.table.horizontalHeader().setFixedHeight(34)
|
||||
header = self.table.horizontalHeader()
|
||||
header.setFixedHeight(34)
|
||||
header.setStretchLastSection(False)
|
||||
header.setSectionResizeMode(4, QHeaderView.ResizeMode.Stretch)
|
||||
header.setSectionResizeMode(10, QHeaderView.ResizeMode.Fixed)
|
||||
self.table.verticalHeader().setDefaultSectionSize(60)
|
||||
self.table.itemSelectionChanged.connect(self._selection_changed)
|
||||
self.table.itemDoubleClicked.connect(lambda _item: self._open_detail())
|
||||
@@ -1051,157 +1001,21 @@ class AppointmentsPage(QWidget):
|
||||
layout.addWidget(self.pager)
|
||||
return card
|
||||
|
||||
def _toggle_video_panel(self, checked: bool) -> None:
|
||||
self._video_panel_user_open = checked
|
||||
self._apply_responsive_layout()
|
||||
|
||||
def _apply_responsive_layout(self) -> None:
|
||||
"""Keep the table usable while retaining a video-list entry at low widths."""
|
||||
"""Collapse only the date filters when horizontal space is limited."""
|
||||
|
||||
panel = getattr(self, "video_panel", None)
|
||||
button = getattr(self, "video_panel_button", None)
|
||||
if not isinstance(panel, QWidget) or not isinstance(button, QPushButton):
|
||||
return
|
||||
narrow = self.width() < 1120
|
||||
for preset in ("yesterday", "day_before", "tomorrow", "day_after"):
|
||||
self.date_buttons[preset].setVisible(not narrow)
|
||||
self.date_overflow_button.setVisible(narrow)
|
||||
self.status_filter_label.setVisible(not narrow)
|
||||
self.confirm_filter_label.setVisible(not narrow)
|
||||
if narrow:
|
||||
if self._responsive_narrow is not True:
|
||||
self._video_panel_user_open = False
|
||||
button.show()
|
||||
button.setChecked(self._video_panel_user_open)
|
||||
panel.setVisible(self._video_panel_user_open)
|
||||
if self._video_panel_user_open:
|
||||
panel.setFixedWidth(max(250, min(300, round(self.width() * 0.32))))
|
||||
else:
|
||||
button.hide()
|
||||
button.setChecked(False)
|
||||
panel.show()
|
||||
panel.setFixedWidth(max(300, min(420, round(self.width() * 0.30))))
|
||||
self._responsive_narrow = narrow
|
||||
|
||||
def resizeEvent(self, event: Any) -> None:
|
||||
super().resizeEvent(event)
|
||||
self._apply_responsive_layout()
|
||||
|
||||
def _sync_video_list(self, rows: Sequence[Any]) -> None:
|
||||
selected_id = (
|
||||
_appointment_id(self.video_list.currentItem().data(Qt.ItemDataRole.UserRole))
|
||||
if self.video_list.currentItem()
|
||||
else 0
|
||||
)
|
||||
self.video_list.clear()
|
||||
row_to_select = -1
|
||||
for index, row in enumerate(rows):
|
||||
status = display_text(first_value(row, "status_desc"), "已预约")
|
||||
name = display_text(first_value(row, "patient_name"), "患者")
|
||||
gender = gender_text(first_value(row, "gender", "gender_desc", default=None))
|
||||
age = first_value(row, "age", default="")
|
||||
identity = " · ".join(
|
||||
part
|
||||
for part in (name, gender, f"{age}岁" if age not in (None, "") else "")
|
||||
if part and part != "—"
|
||||
)
|
||||
assistant = display_text(first_value(row, "assistant_name"), "未分配")
|
||||
item = QListWidgetItem(
|
||||
f"{status} {identity} {assistant}\n"
|
||||
"进入视频问诊 查看 诊单 开方 预约 补全身份"
|
||||
)
|
||||
item.setData(Qt.ItemDataRole.UserRole, row)
|
||||
item.setSizeHint(QSize(0, 84))
|
||||
self.video_list.addItem(item)
|
||||
self.video_list.setItemWidget(item, self._build_video_patient_card(row))
|
||||
if selected_id and _appointment_id(row) == selected_id:
|
||||
row_to_select = index
|
||||
if row_to_select >= 0:
|
||||
self.video_list.setCurrentRow(row_to_select)
|
||||
else:
|
||||
self.video_list.setCurrentRow(-1)
|
||||
self.video_panel_button.setText(f"视频问诊 {len(rows)}")
|
||||
|
||||
def _build_video_patient_card(self, row: Any) -> QWidget:
|
||||
card = QFrame()
|
||||
card.setProperty("videoPatientCard", True)
|
||||
layout = QVBoxLayout(card)
|
||||
layout.setContentsMargins(10, 7, 10, 6)
|
||||
layout.setSpacing(5)
|
||||
|
||||
status_text = display_text(first_value(row, "status_desc"), "已预约")
|
||||
name = display_text(first_value(row, "patient_name"), "患者")
|
||||
gender = gender_text(first_value(row, "gender", "gender_desc", default=None))
|
||||
age = first_value(row, "age", default="")
|
||||
identity = " · ".join(
|
||||
part
|
||||
for part in (name, gender, f"{age}岁" if age not in (None, "") else "")
|
||||
if part and part != "—"
|
||||
)
|
||||
assistant = display_text(first_value(row, "assistant_name"), "未分配")
|
||||
heading = QHBoxLayout()
|
||||
heading.setContentsMargins(0, 0, 0, 0)
|
||||
heading.setSpacing(7)
|
||||
status = QLabel(status_text)
|
||||
status.setProperty("videoStatus", True)
|
||||
status.setProperty("videoStatusKind", "warning" if "挂号" in status_text else "primary")
|
||||
heading.addWidget(status)
|
||||
identity_label = QLabel(identity)
|
||||
identity_label.setProperty("videoIdentity", True)
|
||||
heading.addWidget(identity_label)
|
||||
heading.addStretch(1)
|
||||
assistant_label = QLabel(assistant)
|
||||
assistant_label.setProperty("videoAssistant", True)
|
||||
heading.addWidget(assistant_label)
|
||||
layout.addLayout(heading)
|
||||
|
||||
actions = QHBoxLayout()
|
||||
actions.setContentsMargins(0, 0, 0, 0)
|
||||
actions.setSpacing(1)
|
||||
|
||||
def add_action(
|
||||
label: str,
|
||||
callback: Any,
|
||||
permission: str | None = None,
|
||||
kind: str = "",
|
||||
) -> None:
|
||||
if permission and not _canonical_allowed(self.permissions, permission, default=False):
|
||||
return
|
||||
button = QPushButton(label, card)
|
||||
button.setProperty("videoAction", True)
|
||||
if kind:
|
||||
button.setProperty("videoActionKind", kind)
|
||||
button.clicked.connect(
|
||||
lambda _checked=False, source=row, slot=callback: self._run_video_row_action(
|
||||
source, slot
|
||||
)
|
||||
)
|
||||
actions.addWidget(button)
|
||||
|
||||
add_action(
|
||||
"进入视频问诊",
|
||||
self._request_video,
|
||||
"doctor.appointment/prescription",
|
||||
)
|
||||
add_action("查看", self._open_detail)
|
||||
add_action("诊单", self._edit_patient, "tcm.diagnosis/edit")
|
||||
if can_open_ai_consult(self.permissions):
|
||||
add_action("AI 分析", self._open_ai_consult)
|
||||
add_action("开方", self._open_prescription, "tcm.diagnosis/kaifang")
|
||||
add_action("二维码", self._request_video_qr, "tcm.diagnosis/videoQr", "success")
|
||||
more = QPushButton("更多", card)
|
||||
more.setProperty("videoAction", True)
|
||||
more.setProperty("videoActionKind", "warning")
|
||||
more.clicked.connect(
|
||||
lambda _checked=False, source=row, anchor=more: self._show_video_more_menu(
|
||||
anchor, source
|
||||
)
|
||||
)
|
||||
actions.addWidget(more)
|
||||
actions.addStretch(1)
|
||||
layout.addLayout(actions)
|
||||
return card
|
||||
|
||||
def _run_video_row_action(self, row: Any, callback: Any) -> None:
|
||||
appointment_id = _appointment_id(row)
|
||||
for row_index in range(self.table.rowCount()):
|
||||
@@ -1214,47 +1028,6 @@ class AppointmentsPage(QWidget):
|
||||
break
|
||||
callback()
|
||||
|
||||
def _show_video_more_menu(self, anchor: QPushButton, row: Any) -> None:
|
||||
menu = QMenu(anchor)
|
||||
|
||||
def add_action(label: str, callback: Any, permission: str | None = None) -> None:
|
||||
if permission and not _canonical_allowed(self.permissions, permission, default=False):
|
||||
return
|
||||
action = QAction(label, menu)
|
||||
action.triggered.connect(
|
||||
lambda _checked=False, source=row, slot=callback: self._run_video_row_action(
|
||||
source, slot
|
||||
)
|
||||
)
|
||||
menu.addAction(action)
|
||||
|
||||
add_action("完成问诊", self._complete_selected, "doctor.appointment/complete")
|
||||
add_action("查看病历", self._view_case, "tcm.diagnosis/kaifang")
|
||||
if can_open_ai_consult(self.permissions):
|
||||
add_action("AI 分析", self._open_ai_consult)
|
||||
if can_open_diagnosis_ai_report(self.permissions):
|
||||
add_action("AI 报告", self._open_ai_report)
|
||||
add_action("取消挂号", self._cancel_selected, "doctor.appointment/cancel")
|
||||
if menu.actions():
|
||||
menu.exec(anchor.mapToGlobal(anchor.rect().bottomLeft()))
|
||||
|
||||
def _video_row_selected(self, index: int) -> None:
|
||||
if index < 0:
|
||||
return
|
||||
item = self.video_list.item(index)
|
||||
source = item.data(Qt.ItemDataRole.UserRole) if item is not None else None
|
||||
appointment_id = _appointment_id(source)
|
||||
if appointment_id <= 0:
|
||||
return
|
||||
for row_index in range(self.table.rowCount()):
|
||||
cell = self.table.item(row_index, 0)
|
||||
if (
|
||||
cell is not None
|
||||
and _appointment_id(cell.data(Qt.ItemDataRole.UserRole)) == appointment_id
|
||||
):
|
||||
self.table.selectRow(row_index)
|
||||
break
|
||||
|
||||
def _action_button(
|
||||
self,
|
||||
label: str,
|
||||
@@ -1484,7 +1257,7 @@ class AppointmentsPage(QWidget):
|
||||
self.table.set_rows(rows)
|
||||
self._install_table_selectors()
|
||||
self._install_appointment_info_cells()
|
||||
self._sync_video_list(rows)
|
||||
self._install_im_consult_actions()
|
||||
self._fit_table_rows()
|
||||
for row_index in range(self.table.rowCount()):
|
||||
source_item = self.table.item(row_index, 0)
|
||||
@@ -1634,6 +1407,67 @@ class AppointmentsPage(QWidget):
|
||||
font.setBold(True)
|
||||
patient.setFont(font)
|
||||
|
||||
def _install_im_consult_actions(self) -> None:
|
||||
"""Render a doctor-initiated IM entry for every appointment row."""
|
||||
|
||||
action_column = 10
|
||||
has_permission = _canonical_allowed(
|
||||
self.permissions,
|
||||
"doctor.appointment/prescription",
|
||||
default=False,
|
||||
)
|
||||
for row_index in range(self.table.rowCount()):
|
||||
source_item = self.table.item(row_index, 0)
|
||||
row = source_item.data(Qt.ItemDataRole.UserRole) if source_item is not None else None
|
||||
host = QWidget(self.table)
|
||||
host.setProperty("appointmentImHost", True)
|
||||
host.setAttribute(Qt.WidgetAttribute.WA_StyledBackground, True)
|
||||
layout = QHBoxLayout(host)
|
||||
layout.setContentsMargins(5, 0, 5, 0)
|
||||
layout.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
|
||||
button = QPushButton("IM 问诊", host)
|
||||
button.setObjectName("AppointmentImConsultButton")
|
||||
button.setProperty("appointmentImAction", True)
|
||||
patient_name = display_text(first_value(row, "patient_name"), "患者")
|
||||
button.setAccessibleName(f"与{patient_name}进行 IM 问诊")
|
||||
|
||||
identifiers_ready = (
|
||||
_appointment_id(row) > 0
|
||||
and _diagnosis_id(row) > 0
|
||||
and _video_patient_id(row) > 0
|
||||
)
|
||||
status_error = _im_consult_status_error(row)
|
||||
enabled = (
|
||||
has_permission
|
||||
and self._native_video_capable
|
||||
and identifiers_ready
|
||||
and not status_error
|
||||
)
|
||||
button.setEnabled(enabled)
|
||||
if not has_permission:
|
||||
tooltip = "当前账号无 IM 问诊权限"
|
||||
elif not self._native_video_capable:
|
||||
tooltip = "当前工作站未配置完整的视频问诊能力"
|
||||
elif not identifiers_ready:
|
||||
tooltip = "患者、诊单或挂号标识不完整"
|
||||
elif status_error:
|
||||
tooltip = status_error
|
||||
else:
|
||||
tooltip = "打开患者 IM,可发送消息并从会话中发起视频"
|
||||
button.setToolTip(tooltip)
|
||||
button.clicked.connect(
|
||||
lambda _checked=False, source=row: self._run_video_row_action(
|
||||
source,
|
||||
self._request_video,
|
||||
)
|
||||
)
|
||||
layout.addWidget(button)
|
||||
self.table.setCellWidget(row_index, action_column, host)
|
||||
item = self.table.item(row_index, action_column)
|
||||
if item is not None:
|
||||
item.setText("")
|
||||
|
||||
def _fit_table_rows(self) -> None:
|
||||
"""Give each appointment row enough height for its real line count."""
|
||||
|
||||
@@ -1685,7 +1519,12 @@ class AppointmentsPage(QWidget):
|
||||
and _as_int(first_value(row, "doctor_id", default=0)) > 0
|
||||
)
|
||||
self.call_button.setEnabled(
|
||||
not_completed and self._native_video_capable and _appointment_id(row) > 0
|
||||
has_row
|
||||
and not _im_consult_status_error(row)
|
||||
and self._native_video_capable
|
||||
and _appointment_id(row) > 0
|
||||
and _diagnosis_id(row) > 0
|
||||
and _video_patient_id(row) > 0
|
||||
)
|
||||
self.complete_button.setEnabled(not_completed and _appointment_id(row) > 0)
|
||||
self.prescription_button.setEnabled(has_row and _diagnosis_id(row) > 0)
|
||||
@@ -1760,15 +1599,15 @@ class AppointmentsPage(QWidget):
|
||||
row = self._current_row()
|
||||
if row is None:
|
||||
return
|
||||
status = _status_value(row)
|
||||
if status == 3:
|
||||
show_toast(self, "已完成的挂号不可再发起通话。", "warning")
|
||||
status_error = _im_consult_status_error(row)
|
||||
if status_error:
|
||||
show_toast(self, f"{status_error}。", "warning")
|
||||
return
|
||||
appointment_id = _appointment_id(row)
|
||||
diagnosis_id = _diagnosis_id(row)
|
||||
patient_id = _video_patient_id(row)
|
||||
if appointment_id <= 0 or diagnosis_id <= 0 or patient_id <= 0:
|
||||
show_toast(self, "患者、诊单或挂号标识不完整,无法进入视频问诊。", "warning")
|
||||
show_toast(self, "患者、诊单或挂号标识不完整,无法进入 IM 问诊。", "warning")
|
||||
return
|
||||
self.video_requested.emit(
|
||||
{
|
||||
@@ -1777,6 +1616,8 @@ class AppointmentsPage(QWidget):
|
||||
"patient_id": patient_id,
|
||||
"diagnosis_id": diagnosis_id,
|
||||
"patient_name": first_value(row, "patient_name", default="患者"),
|
||||
# Open IM first. The doctor explicitly starts video from the
|
||||
# conversation instead of requiring an already-live call.
|
||||
"mode": "im",
|
||||
"record": row,
|
||||
}
|
||||
@@ -2069,7 +1910,8 @@ class AppointmentsPage(QWidget):
|
||||
if _diagnosis_id(row) <= 0 or _appointment_id(row) <= 0:
|
||||
show_toast(self, "该预约缺少诊单信息,请先编辑患者。", "warning")
|
||||
return
|
||||
self._begin_prescription_load(row)
|
||||
mode = "view" if prescription_action_label(row) == "查看" else "open"
|
||||
self._begin_prescription_load(row, mode=mode)
|
||||
|
||||
def _view_case(self) -> None:
|
||||
if not _canonical_allowed(self.permissions, "tcm.diagnosis/kaifang", default=False):
|
||||
@@ -2112,7 +1954,7 @@ class AppointmentsPage(QWidget):
|
||||
dialog.diagnosis_requested.connect(self._open_diagnosis_id)
|
||||
dialog.exec()
|
||||
|
||||
def _begin_prescription_load(self, row: Any) -> None:
|
||||
def _begin_prescription_load(self, row: Any, *, mode: str = "open") -> None:
|
||||
snapshot = deepcopy(row)
|
||||
self._prescription_generation += 1
|
||||
generation = self._prescription_generation
|
||||
@@ -2126,18 +1968,51 @@ class AppointmentsPage(QWidget):
|
||||
"get_prescription_by_appointment",
|
||||
appointment_id=appointment_id,
|
||||
),
|
||||
on_success=lambda existing: self._prescription_loaded(existing, snapshot, generation),
|
||||
on_success=lambda existing: self._prescription_loaded(
|
||||
existing,
|
||||
snapshot,
|
||||
generation,
|
||||
mode,
|
||||
),
|
||||
on_error=lambda error: self._prescription_error(error, generation),
|
||||
on_finished=lambda: self._prescription_finished(generation),
|
||||
)
|
||||
|
||||
def _prescription_loaded(self, existing: Any, row: Any, generation: int) -> None:
|
||||
def _prescription_loaded(
|
||||
self,
|
||||
existing: Any,
|
||||
row: Any,
|
||||
generation: int,
|
||||
mode: str = "open",
|
||||
) -> None:
|
||||
if generation != self._prescription_generation:
|
||||
return
|
||||
self.banner.clear()
|
||||
if existing is not None and _as_int(first_value(existing, "id", default=0)) > 0:
|
||||
approved = _as_int(first_value(existing, "audit_status", "status"), -1) == 1
|
||||
voided = _as_int(first_value(existing, "void_status", "is_void"), 0) == 1
|
||||
actual_id = _as_int(first_value(existing, "id", "prescription_id", default=0))
|
||||
expected_id = _as_int(
|
||||
first_value(row, "current_prescription_id", default=0)
|
||||
)
|
||||
actual_appointment_id = _as_int(
|
||||
first_value(existing, "appointment_id", default=0)
|
||||
)
|
||||
expected_appointment_id = _appointment_id(row)
|
||||
identity_changed = (
|
||||
(expected_id > 0 and actual_id != expected_id)
|
||||
or (
|
||||
actual_appointment_id > 0
|
||||
and expected_appointment_id > 0
|
||||
and actual_appointment_id != expected_appointment_id
|
||||
)
|
||||
)
|
||||
if mode == "view" and (not approved or voided or identity_changed):
|
||||
self.banner.show_message(
|
||||
"当前挂号的处方状态已变化,请刷新列表后重试。",
|
||||
"warning",
|
||||
)
|
||||
return
|
||||
if not approved or voided:
|
||||
self._open_existing_prescription_editor(existing)
|
||||
else:
|
||||
@@ -2151,6 +2026,12 @@ class AppointmentsPage(QWidget):
|
||||
dialog.diagnosis_requested.connect(self._open_diagnosis_id)
|
||||
dialog.exec()
|
||||
return
|
||||
if mode == "view":
|
||||
self.banner.show_message(
|
||||
"当前挂号没有可查看的处方,请刷新列表后重试。",
|
||||
"warning",
|
||||
)
|
||||
return
|
||||
self._begin_case_record_load(row)
|
||||
|
||||
def _open_existing_prescription_editor(self, prescription: Any) -> None:
|
||||
|
||||
Reference in New Issue
Block a user