滑动选中

This commit is contained in:
2026-04-24 15:05:57 +08:00
parent bb36bcc645
commit ca1b10c418

View File

@@ -1208,10 +1208,16 @@ export default {
hasChange: false,
hasInit: false,
selectedIds: [],
isMouseSelecting: false,
_selectionSyncToCheckboxesTimer: null,
_onDocumentSelectionChange: null,
_onDocumentMouseUp: null,
_onManuscriptMouseDown: null,
displayList: [],
currentTypeText: '',
tinymceId: this.id || 'vue-tinymce-' + +new Date()
};
},
// this.$nextTick(() => window.tinymce.get(this.tinymceId).setContent(newVal));
@@ -1274,51 +1280,60 @@ export default {
});
this.$refs.scrollDiv.addEventListener('scroll', this.divOnScroll, { passive: true });
document.addEventListener('selectionchange', () => {
if(this.isPreview)return;
const selection = window.getSelection();
if (selection.rangeCount === 0) return;
// document.addEventListener('selectionchange', () => {
// if(this.isPreview)return;
// const selection = window.getSelection();
// if (selection.rangeCount === 0) return;
const range = selection.getRangeAt(0);
// 依然保留 trim() 后的文本判断,用来决定是否显示气泡
const plainText = selection.toString().trim();
// const range = selection.getRangeAt(0);
// // 依然保留 trim() 后的文本判断,用来决定是否显示气泡
// const plainText = selection.toString().trim();
if (plainText !== '' && selection.rangeCount > 0) {
// --- 1. 获取包含标签的 HTML 内容 ---
const fragment = range.cloneContents();
const tempDiv = document.createElement('div');
tempDiv.appendChild(fragment);
// 关键点:这个 label 变量现在包含了完整的 HTML 结构(如 <myfigure>
const htmlLabel = tempDiv.innerHTML;
const allPMainElements = this.getInvolvedPMain(range);
const allIds = [...new Set(allPMainElements.map((el) => el.getAttribute('data-id')))];
if (allIds.length > 0) {
this.updateBubblePosition(range);
const rootItem = this.wordList.find((item) => item.am_id == allIds[0]);
// if (plainText !== '' && selection.rangeCount > 0) {
// // --- 1. 获取包含标签的 HTML 内容 ---
// const fragment = range.cloneContents();
// const tempDiv = document.createElement('div');
// tempDiv.appendChild(fragment);
// // 关键点:这个 label 变量现在包含了完整的 HTML 结构(如 <myfigure>
// const htmlLabel = tempDiv.innerHTML;
// const allPMainElements = this.getInvolvedPMain(range);
// const allIds = [...new Set(allPMainElements.map((el) => el.getAttribute('data-id')))];
// if (allIds.length > 0) {
// this.updateBubblePosition(range);
// const rootItem = this.wordList.find((item) => item.am_id == allIds[0]);
this.currentSelection = {
// 将 label 设置为包含标签的 HTML 字符串
label: htmlLabel,
mainId: allIds[0],
index: this.wordList.indexOf(rootItem),
// this.currentSelection = {
// // 将 label 设置为包含标签的 HTML 字符串
// label: htmlLabel,
// mainId: allIds[0],
// index: this.wordList.indexOf(rootItem),
content: rootItem ? rootItem.content : ''
};
// content: rootItem ? rootItem.content : ''
// };
this.currentId = allIds[0];
this.currentData = rootItem;
}
} else {
this.currentTag = '';
this.currentTagData = {};
this.currentSelection = {
label: '',
mainId: '',
index: 0,
content: {}
};
}
});
// this.currentId = allIds[0];
// this.currentData = rootItem;
// }
// } else {
// this.currentTag = '';
// this.currentTagData = {};
// this.currentSelection = {
// label: '',
// mainId: '',
// index: 0,
// content: {}
// };
// }
// });
this._onDocumentSelectionChange = this.handleDocumentSelectionChange.bind(this);
this._onDocumentMouseUp = this.handleDocumentMouseUp.bind(this);
this._onManuscriptMouseDown = this.handleManuscriptMouseDown.bind(this);
document.addEventListener('selectionchange', this._onDocumentSelectionChange);
document.addEventListener('mouseup', this._onDocumentMouseUp);
if (this.$refs.scroll) {
this.$refs.scroll.addEventListener('mousedown', this._onManuscriptMouseDown);
}
},
activated() {
// 主动触发 MathJax 渲染
@@ -1378,6 +1393,115 @@ export default {
return rangePs;
},
handleManuscriptMouseDown(event) {
if (this.isPreview) return;
const root = this.$refs && this.$refs.scroll;
if (!root) return;
if (root.contains(event.target)) {
this.isMouseSelecting = true;
}
},
handleDocumentMouseUp() {
this.isMouseSelecting = false;
},
handleDocumentSelectionChange() {
if (this.isPreview) return;
const selection = window.getSelection();
if (!selection || selection.rangeCount === 0) return;
const range = selection.getRangeAt(0);
const plainText = selection.toString().trim();
if (plainText !== '' && selection.rangeCount > 0) {
const fragment = range.cloneContents();
const tempDiv = document.createElement('div');
tempDiv.appendChild(fragment);
const htmlLabel = tempDiv.innerHTML;
const allPMainElements = this.getInvolvedPMain(range);
const allIds = [...new Set(allPMainElements.map((el) => el.getAttribute('data-id')))];
if (allIds.length > 0) {
this.updateBubblePosition(range);
const rootItem = this.wordList.find((item) => item.am_id == allIds[0]);
this.currentSelection = {
label: htmlLabel,
mainId: allIds[0],
index: this.wordList.indexOf(rootItem),
content: rootItem ? rootItem.content : ''
};
this.currentId = allIds[0];
this.currentData = rootItem;
}
} else {
this.currentTag = '';
this.currentTagData = {};
this.currentSelection = {
label: '',
mainId: '',
index: 0,
content: {}
};
}
this.scheduleSyncSelectedIdsFromRange();
},
scheduleSyncSelectedIdsFromRange() {
if (this._selectionSyncToCheckboxesTimer) {
clearTimeout(this._selectionSyncToCheckboxesTimer);
}
this._selectionSyncToCheckboxesTimer = setTimeout(() => {
this.syncSelectedIdsFromRangeInternal();
}, 80);
},
syncSelectedIdsFromRangeInternal() {
if (this.isPreview || this.isInternalAction) return;
const scrollRoot = this.$refs && this.$refs.scroll;
if (!scrollRoot) return;
const selection = window.getSelection();
if (!selection || selection.rangeCount === 0 || selection.isCollapsed) return;
const range = selection.getRangeAt(0);
const common = range.commonAncestorContainer;
const commonElement = common && common.nodeType === 1 ? common : common && common.parentElement;
if (!commonElement || !scrollRoot.contains(commonElement)) return;
const nodes = Array.from(scrollRoot.querySelectorAll('.drop-target[main-id]'));
const touchedMainIds = [];
nodes.forEach((node) => {
const id = node.getAttribute('main-id');
if (!id || id === 'References') return;
try {
if (range.intersectsNode(node)) {
touchedMainIds.push(id);
}
} catch (e) {}
});
if (!touchedMainIds.length) return;
const indexMap = {};
this.wordList.forEach((item, idx) => {
if (item && item.am_id != null) {
indexMap[String(item.am_id)] = idx;
}
});
const indices = [...new Set(touchedMainIds.map((id) => indexMap[String(id)]).filter((v) => Number.isInteger(v)))];
if (!indices.length) return;
const lo = Math.min(...indices);
const hi = Math.max(...indices);
const rangeIds = this.wordList
.slice(lo, hi + 1)
.map((item) => (item && item.am_id != null ? item.am_id : null))
.filter((id) => id != null);
if (!rangeIds.length) return;
const existingSet = new Set(this.selectedIds || []);
rangeIds.forEach((id) => existingSet.add(id));
const ordered = this.wordList
.map((item) => (item && item.am_id != null ? item.am_id : null))
.filter((id) => id != null && existingSet.has(id));
this.selectedIds = ordered;
this.$forceUpdate();
},
handleUnbindLink(type) {
const rootItem = this.wordList.find((item) => item.am_id == this.currentTagData.main_id);