diff --git a/src/views/admin/hospitals/components/ServiceRecordDialog.vue b/src/views/admin/hospitals/components/ServiceRecordDialog.vue
index 58daf50..e297ff0 100644
--- a/src/views/admin/hospitals/components/ServiceRecordDialog.vue
+++ b/src/views/admin/hospitals/components/ServiceRecordDialog.vue
@@ -601,8 +601,8 @@ const viewReport = async (filePath) => {
}
.history-list {
- min-height: 80px;
- max-height: 200px;
+ min-height: 80px;
+ max-height: 240px;
overflow-y: auto;
.history-items {
diff --git a/src/views/admin/orders/components/OrderFormDialog.vue b/src/views/admin/orders/components/OrderFormDialog.vue
index c95457e..8016a41 100644
--- a/src/views/admin/orders/components/OrderFormDialog.vue
+++ b/src/views/admin/orders/components/OrderFormDialog.vue
@@ -227,6 +227,20 @@ const form = reactive({
const MAX_FILE_SIZE = 50 * 1024 * 1024;
const MAX_PASTE_IMAGE_SIZE = 5 * 1024 * 1024;
+// 跟踪尚未完成的粘贴图片 FileReader,弹窗关闭/卸载时 abort,
+// 避免 onload 在 editor 已销毁后调用 dangerouslyInsertHtml 导致 Vue 3.5 patch 异常
+const pendingPasteReaders = new Set();
+const cancelPendingPasteReaders = () => {
+ for (const r of pendingPasteReaders) {
+ try {
+ r.abort();
+ } catch (_) {
+ /* ignore */
+ }
+ }
+ pendingPasteReaders.clear();
+};
+
const handleAttachmentChange = (file) => {
if (file?.size > MAX_FILE_SIZE) {
ElMessage.warning('文件不能超过 50MB');
@@ -373,7 +387,9 @@ const handleEditorPasteImage = (editor, event) => {
return;
}
const reader = new FileReader();
+ pendingPasteReaders.add(reader);
reader.onload = (e) => {
+ pendingPasteReaders.delete(reader);
const dataUrl = e.target?.result;
if (!dataUrl) {
ElMessage.error('图片读取失败');
@@ -382,17 +398,37 @@ const handleEditorPasteImage = (editor, event) => {
try {
if (savedSelection) editor.select(savedSelection);
} catch (_) {
+ try {
+ if (typeof editor.focus === 'function') editor.focus();
+ } catch (__) {
+ /* editor 已销毁 */
+ }
+ }
+ // editor 可能已被弹窗关闭流程销毁/卸载,insertHtml 需读套保护
+ try {
if (typeof editor.focus === 'function') editor.focus();
+ editor.dangerouslyInsertHtml(
+ `
`,
+ );
+ if (typeof editor.updateView === 'function') editor.updateView();
+ // ⚠️ 千万别同步 form.description!dangerouslyInsertHtml 是只改 editor 内部、直接操作 Slate 节点的底层 API,
+ // 同步给 v-model 会触发 wangEditor 的自动 setHtml,叠加后让 Slate operations 历史
+ // 里残留旧路径 (如 [0, 2]),下次任何操作就报 "Cannot find a descendant path"。
+ // 正确做法:v-model 保持空,提交时从 editor.getHtml() 取最新内容。
+ } catch (err) {
+ console.warn(
+ '[OrderFormDialog] 粘贴图片插入失败(editor 可能已销毁):',
+ err?.message,
+ );
}
- if (typeof editor.focus === 'function') editor.focus();
- editor.dangerouslyInsertHtml(
- `
`,
- );
- if (typeof editor.updateView === 'function') editor.updateView();
};
reader.onerror = () => {
+ pendingPasteReaders.delete(reader);
ElMessage.error('图片处理失败');
};
+ reader.onabort = () => {
+ pendingPasteReaders.delete(reader);
+ };
reader.readAsDataURL(file);
});
return false;
@@ -414,6 +450,19 @@ const handleEditorCreated = (editor) => {
};
const resetForm = () => {
+ // 显式清空 wangEditor 内部内容和 Slate selection 状态。
+ // 原因:paste 图片后 editor 内部有内容,v-show 下弹窗不卸载组件,
+ // 二次打开时不调用 setHtml 会导致 Slate 内部残留旧路径的 selection。
+ const editor = editorRef.value;
+ if (editor) {
+ try {
+ if (typeof editor.setHtml === 'function') {
+ editor.setHtml('');
+ }
+ } catch (_) {
+ /* editor 已销毁或不可用 */
+ }
+ }
Object.assign(form, {
hospitalId: undefined,
title: '',
@@ -485,6 +534,8 @@ watch(
const closeDialog = () => emit('update:modelValue', false);
const onClosed = () => {
+ // 弹窗关闭时 abort 未完成的粘贴 FileReader,防止 onload 跨生命周期访问已销毁的 editor
+ cancelPendingPasteReaders();
resetForm();
emit('closed');
};
@@ -493,6 +544,13 @@ const onSubmit = async () => {
// dept / submitter 去掉两端空格后再校验,避免仅含空格的"伪必填"绕过
form.department = (form.department || '').trim();
form.submitter = (form.submitter || '').trim();
+ // 提交前从 wangEditor 同步富文本内容到 form.description。
+ // dangerouslyInsertHtml 只改 editor 内部不触发 v-model,避免了上一轮踩的坑,
+ // 代价是 form.description 不会随粘贴自动更新,所以提交时从 getHtml() 拿最新内容。
+ const editor = editorRef.value;
+ if (editor && typeof editor.getHtml === 'function') {
+ form.description = editor.getHtml() || '';
+ }
await formRef.value.validate(async (valid) => {
if (!valid) return;
const formData = new FormData();
@@ -521,6 +579,7 @@ const removePasteListener = () => {
};
onBeforeUnmount(() => {
+ cancelPendingPasteReaders();
removePasteListener();
pasteHandlerRef = null;
const editor = editorRef.value;
diff --git a/src/views/admin/orders/list.vue b/src/views/admin/orders/list.vue
index 5f85389..22f7260 100644
--- a/src/views/admin/orders/list.vue
+++ b/src/views/admin/orders/list.vue
@@ -91,7 +91,7 @@
-
+
@@ -151,7 +151,7 @@
+ @submit="onQuickSubmit" @closed="console.log('close')" />
diff --git a/src/views/hospital/orders/list.vue b/src/views/hospital/orders/list.vue
index 5b51479..93cf820 100644
--- a/src/views/hospital/orders/list.vue
+++ b/src/views/hospital/orders/list.vue
@@ -66,7 +66,7 @@
-->
-
+
{{ row.priority }}